mirror of
https://codeup.aliyun.com/67762337eccfc218f6110e0e/per-boe/java-servers.git
synced 2025-12-17 23:06:53 +08:00
Compare commits
27 Commits
9f26b991a0
...
251208-fea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0644706aca | ||
|
|
68166f6cf1 | ||
|
|
317bdb161c | ||
|
|
3de308fee1 | ||
|
|
0981eaaf52 | ||
|
|
639ac253f1 | ||
|
|
f43d52c210 | ||
|
|
c01a69c9df | ||
|
|
eba13bb602 | ||
|
|
240d4725e6 | ||
|
|
0b5716ed7d | ||
|
|
a1121e8700 | ||
|
|
5f42139407 | ||
|
|
3d35519615 | ||
|
|
e76833a3c2 | ||
|
|
c3027517c5 | ||
|
|
ce2524fdcb | ||
|
|
4f53a268ba | ||
|
|
8e1a68d416 | ||
|
|
4e6d1a6b04 | ||
|
|
f506fe49f2 | ||
|
|
3c582e9ade | ||
|
|
716ba91c2e | ||
|
|
f39562ffc9 | ||
|
|
2b7d0ef35a | ||
|
|
7e21784a74 | ||
|
|
4b064171e6 |
@@ -35,6 +35,16 @@ public class UrlSecurityFilterImpl implements IUrlSecurityFilter{
|
||||
|
||||
noLoginUrls.add("/xboe/m/course/manage/test");
|
||||
noLoginUrls.add("/xboe/m/course/manage/redirectDetail");
|
||||
|
||||
noLoginUrls.add("/xboe/account/update-avatar");
|
||||
|
||||
// 全量测试
|
||||
noLoginUrls.add("/xboe/m/course/content/save");
|
||||
|
||||
// 新的测试接口
|
||||
noLoginUrls.add("/xboe/m/course/content/courseware/save");
|
||||
noLoginUrls.add("/xboe/m/course/content/courseware/atomic-upload");
|
||||
noLoginUrls.add("/xboe/m/course/content/courseware/hierarchical-upload");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,24 +1,30 @@
|
||||
package com.xboe.module.course.api;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import javax.annotation.Resource;import com.xboe.module.course.dao.CourseDao;
|
||||
import com.xboe.module.course.dto.*;
|
||||
import com.xboe.module.course.service.*;
|
||||
import com.xboe.module.course.vo.TypeTreeVo;
|
||||
import com.xboe.system.organization.vo.OrganizationVo;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import com.xboe.common.OrderCondition;
|
||||
import com.xboe.common.utils.StringUtil;
|
||||
import com.xboe.core.log.AutoLog;
|
||||
import com.xboe.core.orm.FieldFilters;
|
||||
import com.xboe.module.course.dao.CourseContentDao;
|
||||
import com.xboe.module.course.dao.CourseSectionDao;
|
||||
import com.xboe.module.course.entity.*;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import com.xboe.core.JsonResponse;
|
||||
import com.xboe.core.api.ApiBaseController;
|
||||
import com.xboe.module.course.dto.CourseContentDto;
|
||||
import com.xboe.module.course.dto.SortItem;
|
||||
import com.xboe.module.course.entity.CourseAssess;
|
||||
import com.xboe.module.course.entity.CourseExam;
|
||||
import com.xboe.module.course.entity.CourseHomeWork;
|
||||
import com.xboe.module.course.entity.CourseSection;
|
||||
import com.xboe.module.course.service.ICourseContentService;
|
||||
import com.xboe.module.course.service.ICourseSectionService;
|
||||
import com.xboe.standard.BaseConstant;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -40,10 +46,31 @@ public class CourseContentApi extends ApiBaseController{
|
||||
@Resource
|
||||
ICourseContentService ccontentService;
|
||||
|
||||
@Resource
|
||||
ICourseContentService courseWareService;
|
||||
|
||||
@Resource
|
||||
CourseSectionDao csectionDao; @Resource
|
||||
TypeTreeService typeTreeService;
|
||||
|
||||
@Resource
|
||||
CourseDao courseDao;
|
||||
|
||||
@Resource
|
||||
private IAtomicBatchUploadService atomicBatchUploadService;
|
||||
|
||||
@Resource
|
||||
private CourseContentValidationService courseContentValidationService;
|
||||
|
||||
|
||||
// 测试导入包
|
||||
@Resource
|
||||
private CourseContentDao ccDao;
|
||||
|
||||
|
||||
/**
|
||||
* 获取课程的作业信息
|
||||
* @param cid
|
||||
* @param ccid
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping(value="/homework",method= {RequestMethod.POST,RequestMethod.GET})
|
||||
@@ -159,6 +186,9 @@ public class CourseContentApi extends ApiBaseController{
|
||||
return badRequest("无课程关联");
|
||||
}
|
||||
try {
|
||||
// 测试
|
||||
// log.info("CourseContentDto: {}", cc);
|
||||
|
||||
ccontentService.saveOrUpdate(cc);
|
||||
return success(cc);
|
||||
}catch(Exception e) {
|
||||
@@ -168,6 +198,390 @@ public class CourseContentApi extends ApiBaseController{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存课件内容,同时把保存后的内容返回给前端,以便前端应用转化
|
||||
* @param cws
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/courseware/save")
|
||||
public JsonResponse<CourseWareSaveDto> saveCourseWare(@RequestBody CourseWareSaveDto cws) {
|
||||
|
||||
// 1. 参数基础校验
|
||||
if (cws.getCourse() == null) {
|
||||
return badRequest("课程基础信息不能为空");
|
||||
}
|
||||
|
||||
// 课程名称必填校验
|
||||
if (StringUtils.isBlank(cws.getCourse().getName())) {
|
||||
return badRequest("课程名称不能为空");
|
||||
}
|
||||
|
||||
// 课程名称长度校验
|
||||
if (cws.getCourse().getName().length() > 100) {
|
||||
return badRequest("课程名称长度不能超过100字");
|
||||
}
|
||||
|
||||
if (CollectionUtils.isEmpty(cws.getTeacherList())) {
|
||||
return badRequest("至少选择一位授课教师");
|
||||
}
|
||||
|
||||
if (cws.getSysType1() == null || StringUtils.isBlank(cws.getSysType1().getId())) {
|
||||
return badRequest("课程一级分类不能为空");
|
||||
}
|
||||
|
||||
if (cws.getResOwner1() == null || StringUtils.isBlank(cws.getResOwner1().getId())) {
|
||||
return badRequest("资源一级归属不能为空");
|
||||
}
|
||||
|
||||
if (cws.getDevice() == null || (cws.getDevice() < 1 || cws.getDevice() > 3)) {
|
||||
return badRequest("观看设置参数错误,必须是1、2或3");
|
||||
}
|
||||
|
||||
// 2. 业务逻辑处理
|
||||
try {
|
||||
// 记录操作日志
|
||||
String courseName = cws.getCourse().getId();
|
||||
log.info("开始保存/更新课程课件,课程ID:{}", courseName);
|
||||
|
||||
// 调用ICourseContentService的saveOrUpdateCourseware方法
|
||||
ccontentService.saveOrUpdateCourseware(cws);
|
||||
|
||||
// 操作成功,返回原DTO给前端
|
||||
log.info("课程课件保存/更新成功,课程ID:{}", courseName);
|
||||
return success(cws, "保存成功");
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("保存课件内容错误,课程名称:{}", cws.getCourse().getName(), e);
|
||||
return error("保存失败:" + e.getMessage(), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 信息批量上传(扁平化传参方式)
|
||||
*/
|
||||
@PostMapping("/courseware/atomic-upload")
|
||||
public JsonResponse<BatchUploadResponseDto> atomicBatchUpload(
|
||||
@Validated @RequestBody BatchUploadWithNullDto uploadDto) {
|
||||
|
||||
try {
|
||||
log.info("接收批量上传请求,课程ID: {}, 操作类型: {}, 内容项数量: {}",
|
||||
uploadDto.getCourseId(),
|
||||
uploadDto.getOperationType(),
|
||||
uploadDto.getContentItems() != null ? uploadDto.getContentItems().size() : 0);
|
||||
|
||||
// 基础验证
|
||||
if (StringUtils.isBlank(uploadDto.getCourseId())) {
|
||||
return badRequest("课程ID不能为空");
|
||||
}
|
||||
|
||||
if (uploadDto.getOperationType() == null) {
|
||||
return badRequest("操作类型不能为空");
|
||||
}
|
||||
|
||||
if (uploadDto.getOperationType() != 1 ) {
|
||||
return badRequest("操作类型必须是1(新增)");
|
||||
}
|
||||
|
||||
if (CollectionUtils.isEmpty(uploadDto.getContentItems())) {
|
||||
return badRequest("内容项列表不能为空");
|
||||
}
|
||||
|
||||
// 验证每个内容项(使用服务层的验证方法)
|
||||
List<String> validationErrors = courseContentValidationService.validateBatchUpload(uploadDto);
|
||||
if (CollectionUtils.isNotEmpty(validationErrors)) {
|
||||
return badRequest(String.join("; ", validationErrors));
|
||||
}
|
||||
|
||||
// 验证章节结构和资源唯一性
|
||||
List<String> structureErrors = validateChapterSectionStructure(uploadDto);
|
||||
if (CollectionUtils.isNotEmpty(structureErrors)) {
|
||||
return badRequest(String.join("; ", structureErrors));
|
||||
}
|
||||
|
||||
// 执行批量上传
|
||||
BatchUploadResponseDto response = atomicBatchUploadService.atomicBatchUpload(uploadDto);
|
||||
|
||||
log.info("批量上传成功,课程ID: {}, 成功: {}, 失败: {}",
|
||||
uploadDto.getCourseId(), response.getSuccessCount(), response.getFailCount());
|
||||
|
||||
return success(response, "原子批量上传成功");
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
log.error("原子批量上传失败", e);
|
||||
return error("原子批量上传失败: " + e.getMessage());
|
||||
} catch (Exception e) {
|
||||
log.error("系统异常", e);
|
||||
return error("系统异常: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存课程结构(课程->章->节->内容)
|
||||
*/
|
||||
@PostMapping("/courseware/hierarchical-upload")
|
||||
public JsonResponse<BatchUploadResponseDto> hierarchicalUpload(
|
||||
@Validated @RequestBody HierarchicalCourseDto hierarchicalDto) {
|
||||
|
||||
try {
|
||||
log.info("接收层级结构上传请求,课程ID: {}, 章节数量: {}",
|
||||
hierarchicalDto.getCourseId(),
|
||||
hierarchicalDto.getChapters() != null ? hierarchicalDto.getChapters().size() : 0);
|
||||
|
||||
// 层级结构->扁平结构
|
||||
BatchUploadWithNullDto batchDto = convertToBatchUploadDto(hierarchicalDto);
|
||||
|
||||
// 基础验证
|
||||
if (StringUtils.isBlank(batchDto.getCourseId())) {
|
||||
return badRequest("课程ID不能为空");
|
||||
}
|
||||
|
||||
if (batchDto.getOperationType() == null) {
|
||||
return badRequest("操作类型不能为空");
|
||||
}
|
||||
|
||||
if (batchDto.getOperationType() != 1 ) {
|
||||
return badRequest("操作类型必须是1(新增)");
|
||||
}
|
||||
|
||||
if (CollectionUtils.isEmpty(batchDto.getContentItems())) {
|
||||
return badRequest("内容项列表不能为空");
|
||||
}
|
||||
|
||||
// 验证每个内容项
|
||||
List<String> validationErrors = courseContentValidationService.validateBatchUpload(batchDto);
|
||||
if (CollectionUtils.isNotEmpty(validationErrors)) {
|
||||
return badRequest(String.join("; ", validationErrors));
|
||||
}
|
||||
|
||||
// 验证章节结构和资源唯一性
|
||||
List<String> structureErrors = validateChapterSectionStructure(batchDto);
|
||||
if (CollectionUtils.isNotEmpty(structureErrors)) {
|
||||
return badRequest(String.join("; ", structureErrors));
|
||||
}
|
||||
|
||||
// 执行批量上传
|
||||
BatchUploadResponseDto response = atomicBatchUploadService.atomicBatchUpload(batchDto);
|
||||
|
||||
log.info("层级结构上传成功,课程ID: {}, 成功: {}, 失败: {}",
|
||||
batchDto.getCourseId(), response.getSuccessCount(), response.getFailCount());
|
||||
|
||||
return success(response, "层级结构上传成功");
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
log.error("层级结构上传失败", e);
|
||||
return error("层级结构上传失败: " + e.getMessage());
|
||||
} catch (Exception e) {
|
||||
log.error("系统异常", e);
|
||||
return error("系统异常: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将层级结构DTO转换为扁平结构DTO
|
||||
*/
|
||||
private BatchUploadWithNullDto convertToBatchUploadDto(HierarchicalCourseDto hierarchicalDto) {
|
||||
BatchUploadWithNullDto batchDto = new BatchUploadWithNullDto();
|
||||
batchDto.setCourseId(hierarchicalDto.getCourseId());
|
||||
batchDto.setOperationType(hierarchicalDto.getOperationType() != null ?
|
||||
hierarchicalDto.getOperationType() : 1); // 默认为新增操作
|
||||
batchDto.setOwnerInfo(hierarchicalDto.getOwnerInfo());
|
||||
|
||||
List<BatchUploadWithNullDto.ContentItem> contentItems = new ArrayList<>();
|
||||
|
||||
// 遍历章节和节
|
||||
for (HierarchicalCourseDto.Chapter chapter : hierarchicalDto.getChapters()) {
|
||||
for (HierarchicalCourseDto.Section section : chapter.getSections()) {
|
||||
BatchUploadWithNullDto.ContentItem contentItem = section.getContent();
|
||||
contentItem.setCsectionId(chapter.getChapterId()); // 章ID
|
||||
contentItems.add(contentItem);
|
||||
}
|
||||
}
|
||||
|
||||
batchDto.setContentItems(contentItems);
|
||||
return batchDto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证章节结构和资源唯一性
|
||||
* 确保:
|
||||
* 1. 一个课程下面对应多个章
|
||||
* 2. 一个章底下对应多个节
|
||||
* 3. 一个节底下对应一个资源
|
||||
* 4. 每个节只能上传一种资源
|
||||
*
|
||||
* @param uploadDto 批量上传DTO
|
||||
* @return 验证错误列表
|
||||
*/
|
||||
private List<String> validateChapterSectionStructure(BatchUploadWithNullDto uploadDto) {
|
||||
List<String> errors = new ArrayList<>();
|
||||
|
||||
// 检查是否有重复的章节ID
|
||||
Map<String, List<BatchUploadWithNullDto.ContentItem>> sectionContentMap = new HashMap<>();
|
||||
|
||||
for (int i = 0; i < uploadDto.getContentItems().size(); i++) {
|
||||
BatchUploadWithNullDto.ContentItem item = uploadDto.getContentItems().get(i);
|
||||
|
||||
// 检查是否指定章节ID
|
||||
if (StringUtils.isBlank(item.getCsectionId())) {
|
||||
errors.add("第" + (i + 1) + "项:必须指定章节ID");
|
||||
continue;
|
||||
}
|
||||
|
||||
// 将内容项按照章节ID分组
|
||||
sectionContentMap.computeIfAbsent(item.getCsectionId(), k -> new ArrayList<>()).add(item);
|
||||
}
|
||||
|
||||
// 检查每个章节是否只有一个资源
|
||||
for (Map.Entry<String, List<BatchUploadWithNullDto.ContentItem>> entry : sectionContentMap.entrySet()) {
|
||||
String sectionId = entry.getKey();
|
||||
List<BatchUploadWithNullDto.ContentItem> itemsInSection = entry.getValue();
|
||||
|
||||
if (itemsInSection.size() > 1) {
|
||||
errors.add("章节ID " + sectionId + " 下存在 " + itemsInSection.size() + " 个资源,每个章节只能有一个资源");
|
||||
}
|
||||
}
|
||||
|
||||
// 验证章节ID是否存在+属于当前课程
|
||||
if (errors.isEmpty()) {
|
||||
List<String> sectionIds = new ArrayList<>(sectionContentMap.keySet());
|
||||
long validSections = csectionDao.count(
|
||||
FieldFilters.eq("courseId", uploadDto.getCourseId()),
|
||||
FieldFilters.in("id", sectionIds)
|
||||
);
|
||||
|
||||
if (validSections != sectionIds.size()) {
|
||||
errors.add("部分章节ID无效或不属于当前课程");
|
||||
}
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取课程分类树(多级联查)
|
||||
*/
|
||||
@GetMapping("/type/tree")
|
||||
public JsonResponse<List<TypeTreeVo>> getCourseTypeTree() {
|
||||
try {
|
||||
List<TypeTreeVo> typeTree = typeTreeService.getCourseTypeTree();
|
||||
return success(typeTree, "获取课程分类树成功");
|
||||
} catch (Exception e) {
|
||||
log.error("获取课程分类树失败", e);
|
||||
return error("获取课程分类树失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取资源归属树(多级联查)
|
||||
*/
|
||||
@GetMapping("/owner/tree")
|
||||
public JsonResponse<List<OrganizationVo>> getResourceOwnerTree() {
|
||||
try {
|
||||
List<OrganizationVo> orgTree = typeTreeService.getResourceOwnerTree();
|
||||
return success(orgTree, "获取资源归属树成功");
|
||||
} catch (Exception e) {
|
||||
log.error("获取资源归属树失败", e);
|
||||
return error("获取资源归属树失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据父级ID获取子分类
|
||||
*/
|
||||
@GetMapping("/type/children")
|
||||
public JsonResponse<List<TypeTreeVo>> getChildTypes(@RequestParam String parentId) {
|
||||
try {
|
||||
if (StringUtils.isBlank(parentId)) {
|
||||
return badRequest("父级ID不能为空");
|
||||
}
|
||||
List<TypeTreeVo> children = typeTreeService.getChildTypes(parentId);
|
||||
return success(children, "获取子分类成功");
|
||||
} catch (Exception e) {
|
||||
log.error("获取子分类失败", e);
|
||||
return error("获取子分类失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据父级ID获取子组织
|
||||
*/
|
||||
@GetMapping("/owner/children")
|
||||
public JsonResponse<List<OrganizationVo>> getChildOrgs(@RequestParam String parentId) {
|
||||
try {
|
||||
if (StringUtils.isBlank(parentId)) {
|
||||
return badRequest("父级ID不能为空");
|
||||
}
|
||||
List<OrganizationVo> children = typeTreeService.getChildOrgs(parentId);
|
||||
return success(children, "获取子组织成功");
|
||||
} catch (Exception e) {
|
||||
log.error("获取子组织失败", e);
|
||||
return error("获取子组织失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据课程名称查询课程
|
||||
*/
|
||||
@GetMapping("/by-name")
|
||||
public JsonResponse<Course> getCourseByName(@RequestParam String name) {
|
||||
try {
|
||||
if (StringUtils.isBlank(name)) {
|
||||
return badRequest("课程名称不能为空");
|
||||
}
|
||||
|
||||
Course course = courseDao.findOne(
|
||||
FieldFilters.eq("name", name),
|
||||
FieldFilters.eq("deleted", false)
|
||||
);
|
||||
|
||||
if (course == null) {
|
||||
return success(null, "课程不存在");
|
||||
}
|
||||
|
||||
return success(course, "查询成功");
|
||||
} catch (Exception e) {
|
||||
log.error("根据课程名称查询失败", e);
|
||||
return error("查询失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
// 测试接口
|
||||
@PostMapping("/test")
|
||||
public JsonResponse<List<CourseContentDto>> test() {
|
||||
try {
|
||||
// 1. 查询系统中所有未被删除的课程内容(核心全量数据)
|
||||
List<CourseContent> allContentList = ccDao.findList(
|
||||
OrderCondition.asc("courseId"),
|
||||
// 按课程ID+排序号排序
|
||||
FieldFilters.eq("deleted", false) // 过滤已删除数据
|
||||
);
|
||||
|
||||
// 2. 无数据时返回空列表+提示
|
||||
if (allContentList.isEmpty()) {
|
||||
return success(Collections.emptyList(), "系统中暂无课程内容数据");
|
||||
}
|
||||
|
||||
// 3. 组装每个课程内容的完整DTO(关联作业/考试/评估)
|
||||
List<CourseContentDto> allDtoList = new ArrayList<>();
|
||||
for (CourseContent content : allContentList) {
|
||||
CourseContentDto dto = new CourseContentDto();
|
||||
dto.setContent(content); // 课程内容基础数据
|
||||
dto.setHomework(ccontentService.getHomework(content.getId())); // 关联作业
|
||||
dto.setExam(ccontentService.getExam(content.getId())); // 关联考试
|
||||
dto.setAssess(ccontentService.getAssess(content.getId())); // 关联评估
|
||||
allDtoList.add(dto);
|
||||
}
|
||||
|
||||
log.info("全查询返回数据条数:{}", allDtoList.size());
|
||||
|
||||
// 5. 返回全量数据
|
||||
return success(allDtoList, "全查询成功,共返回 " + allDtoList.size() + " 条课程内容数据");
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("全查询课程内容数据失败", e);
|
||||
return error("全查询失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/save-section")
|
||||
public JsonResponse<CourseSection> saveSection(CourseSection section){
|
||||
if(StringUtils.isBlank(section.getCourseId())) {
|
||||
|
||||
@@ -68,6 +68,9 @@ public class CourseManageApi extends ApiBaseController{
|
||||
@Autowired
|
||||
IOutSideDataService outsideService;
|
||||
|
||||
@Autowired
|
||||
ICourseManageService courseManageService;
|
||||
|
||||
|
||||
@Resource
|
||||
IEmailService service;
|
||||
@@ -131,6 +134,16 @@ public class CourseManageApi extends ApiBaseController{
|
||||
return success(coursePageService.pageQuery(null, coursePageQueryDTO));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新-教师端 我开发的课程
|
||||
* @param coursePageQueryDTO
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/develop_page")
|
||||
public JsonResponse<PageList<CoursePageVo>> teacherPage(@RequestBody CoursePageQueryDTO coursePageQueryDTO) {
|
||||
return success(coursePageService.pageQuery(getCurrent(), coursePageQueryDTO));
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前用户是否在管理端显示置顶相关功能
|
||||
* @return
|
||||
@@ -678,6 +691,81 @@ public class CourseManageApi extends ApiBaseController{
|
||||
return error("提交课程处理失败",e.getMessage());
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 25.12.10新增,提交审核到BPM
|
||||
*暂无BPM接口
|
||||
*非空判断需要加/日志
|
||||
*/
|
||||
@PostMapping("/bpm-submit")
|
||||
@AutoLog(module = "课程",action = "提交审核到BPM",info = "")
|
||||
public JsonResponse<String> submitBPMCourseFull(@RequestBody CourseFullDto dto){
|
||||
try {
|
||||
BPMResponseDto response = null;
|
||||
//首先判断是否为停用审核
|
||||
if(dto.getAuditType() != null && dto.getAuditType()==0)
|
||||
{
|
||||
// 准备停用审核的JSON请求体,暂时先按照京东方大学堂后端调用BPM系统需要的接口文档的入参示例完成,等到有外部接口时再修改
|
||||
String jsonRequestBody = courseManageService.prepareDisableAuditRequest(dto);
|
||||
// TODO: 调用BPM接口
|
||||
//BPMResponseDto bpmResponsedto= courseManageService.callBPMInterface(jsonRequestBody);
|
||||
}
|
||||
//再判断是否为启用审核
|
||||
else if(dto.getAuditType() != null && dto.getAuditType()==3)
|
||||
{
|
||||
// 准备启用审核的JSON请求体,暂时先按照京东方大学堂后端调用BPM系统需要的接口文档的入参示例完成,等到有外部接口时再修改
|
||||
String jsonRequestBody = courseManageService.prepareDisableAuditRequest(dto);
|
||||
// TODO: 调用BPM接口
|
||||
// BPMResponseDto bpmResponsedto= courseManageService.callBPMInterface(jsonRequestBody);
|
||||
}
|
||||
else{
|
||||
//通过查看在boe_course_hrbp_audit表当中,有没有旧的审核记录数据,判断为创建审核还是编辑审核
|
||||
String courseId=dto.getCourse().getId();
|
||||
CourseHRBPAudit previousAudit = hrbpAuditService.hadAuditing(courseId);
|
||||
if (previousAudit != null) {
|
||||
// 存在历史审核记录,视为编辑审核
|
||||
//编辑课程审核,需要后端服务整理出本次编辑的内容与编辑前的前后差异,提交至BPM系统。
|
||||
dto.setAuditType(2);
|
||||
} else {
|
||||
// 无历史审核记录,视为创建审核
|
||||
dto.setAuditType(1);
|
||||
// 准备创建审核的JSON请求体,暂时先按照京东方大学堂后端调用BPM系统需要的接口文档的入参示例完成,等到有外部接口时再修改
|
||||
String jsonRequestBody = courseManageService.prepareCreateAuditRequest(dto);
|
||||
// TODO: 调用BPM接口
|
||||
// BPMResponseDto bpmResponsedto= courseManageService.callBPMInterface(jsonRequestBody);
|
||||
}
|
||||
}
|
||||
// 构造DTO
|
||||
//实际使用中,返回的值从BPM接口返回的JSON中获取
|
||||
BPMResponseDto bpmResponsedto = new BPMResponseDto();
|
||||
bpmResponsedto.setStatus("success");
|
||||
bpmResponsedto.setAuditId("audit123456");
|
||||
bpmResponsedto.setAuditApprover("管理员hrbp");
|
||||
return success("success");
|
||||
} catch (Exception e) {
|
||||
log.error("提交保存课程信息错误",e);
|
||||
return error("error");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 25.12.12新增,审核完成的回调接口
|
||||
* 注意需和项目经理核对:
|
||||
* boe_course_HRBP_audit表里没有直接对应的字段,原表auditUser和lastAuditUser,原表auditRemark/lastRemark和auditComment
|
||||
*/
|
||||
@PostMapping("/audit/callback")
|
||||
public JsonResponse<String> callbackBPM(@RequestBody BPMCallbackDto dto){
|
||||
try {
|
||||
if(StringUtils.isBlank(dto.getAuditId()))
|
||||
{
|
||||
log.error("BPM回调失败:auditId不能为空");
|
||||
return error("auditId不能为空");
|
||||
}
|
||||
hrbpAuditService.bpmRecallUpdate(dto);
|
||||
return success("BPM回调成功");
|
||||
} catch (Exception e) {
|
||||
log.error("回调错误",e);
|
||||
return error("error");
|
||||
}
|
||||
}
|
||||
|
||||
private String createEmailHtml(String name,String orgId, String orgName,String createBy,String courseName) throws Exception {
|
||||
StringBuffer htmlMsg=new StringBuffer("<div style=\"line-height:30px;border:2px solid #2990ca;padding:20px\">");
|
||||
|
||||
@@ -6,6 +6,7 @@ import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import com.xboe.module.course.dto.CoursePageQueryDTO;
|
||||
@@ -30,6 +31,47 @@ import javax.persistence.Query;
|
||||
public class CourseDao extends BaseDao<Course> {
|
||||
@PersistenceContext
|
||||
private EntityManager entityManager;
|
||||
|
||||
/**
|
||||
* 单条课程查询
|
||||
* @param filters 查询条件
|
||||
* @return Course对象
|
||||
*/
|
||||
public Course findOneWithoutDuration(List<IFieldFilter> filters) throws Exception {
|
||||
QueryBuilder query = QueryBuilder.from(Course.class);
|
||||
// 只查询必要字段,彻底排除courseDuration
|
||||
query.addFields("id", "name", "deleted", "status", "published", "enabled", "orgId", "sysCreateBy");
|
||||
if (filters != null && !filters.isEmpty()) {
|
||||
query.addFilters(filters);
|
||||
}
|
||||
// 只查1条
|
||||
query.setPageSize(1);
|
||||
List<Object[]> list = this.findListFields(query.builder());
|
||||
if (list.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
// 封装为Course对象(仅赋值查询的字段)
|
||||
Object[] objs = list.get(0);
|
||||
Course course = new Course();
|
||||
course.setId((String) objs[0]);
|
||||
course.setName((String) objs[1]);
|
||||
course.setDeleted((Boolean) objs[2]);
|
||||
course.setStatus((Integer) objs[3]);
|
||||
course.setPublished((Boolean) objs[4]);
|
||||
course.setEnabled((Boolean) objs[5]);
|
||||
course.setOrgId((String) objs[6]);
|
||||
course.setSysCreateBy((String) objs[7]);
|
||||
return course;
|
||||
}
|
||||
|
||||
// 按课程名称查询
|
||||
public Course findByNameWithoutDuration(String courseName) throws Exception {
|
||||
List<IFieldFilter> filters = new ArrayList<>();
|
||||
filters.add(FieldFilters.eq("name", courseName));
|
||||
filters.add(FieldFilters.eq("deleted", false));
|
||||
return findOneWithoutDuration(filters);
|
||||
}
|
||||
|
||||
/**
|
||||
* 课程分页 搜索查询
|
||||
* */
|
||||
@@ -101,6 +143,7 @@ public class CourseDao extends BaseDao<Course> {
|
||||
StringBuilder builder = new StringBuilder("select ");
|
||||
builder.append("c.id,");
|
||||
builder.append("c.name,");
|
||||
builder.append("c.type,");
|
||||
builder.append("c.cover_img AS coverImg,");
|
||||
builder.append("c.sys_type1 AS sysType1,");
|
||||
builder.append("c.sys_type2 AS sysType2,");
|
||||
@@ -122,7 +165,8 @@ public class CourseDao extends BaseDao<Course> {
|
||||
builder.append("c.open_course AS openCourse,");
|
||||
builder.append("c.is_top AS isTop,");
|
||||
builder.append("COALESCE(tch.teacher_names, '') AS teacherName,");
|
||||
builder.append("c.sort_weight AS sortWeight");
|
||||
builder.append("c.sort_weight AS sortWeight,");
|
||||
builder.append("c.sys_update_time AS sysUpdateTime");
|
||||
// 拼接FROM及查询条件语句
|
||||
appendFrom(builder, queryDTO, isSystemAdmin, orgIds, currentAccountId, userCenterSchema);
|
||||
// 排序语句
|
||||
@@ -142,59 +186,65 @@ public class CourseDao extends BaseDao<Course> {
|
||||
}
|
||||
|
||||
vo.setName((String) row[1]);
|
||||
vo.setCoverImg((String) row[2]);
|
||||
vo.setSysType1((String) row[3]);
|
||||
vo.setSysType2((String) row[4]);
|
||||
vo.setSysType3((String) row[5]);
|
||||
vo.setOrgId((String) row[6]);
|
||||
vo.setOrgName((String) row[7]);
|
||||
vo.setOrgFullName((String) row[8]);
|
||||
vo.setSysCreateBy((String) row[9]);
|
||||
vo.setCreateFrom((String) row[10]);
|
||||
vo.setType((Integer) row[2]);
|
||||
vo.setCoverImg((String) row[3]);
|
||||
vo.setSysType1((String) row[4]);
|
||||
vo.setSysType2((String) row[5]);
|
||||
vo.setSysType3((String) row[6]);
|
||||
vo.setOrgId((String) row[7]);
|
||||
vo.setOrgName((String) row[8]);
|
||||
vo.setOrgFullName((String) row[9]);
|
||||
vo.setSysCreateBy((String) row[10]);
|
||||
vo.setCreateFrom((String) row[11]);
|
||||
|
||||
// 增加对Timestamp和LocalDateTime的兼容性,防止Timestamp为null的情况
|
||||
Timestamp sysCreateTimestamp = (Timestamp) row[11];
|
||||
Timestamp sysCreateTimestamp = (Timestamp) row[12];
|
||||
if (sysCreateTimestamp != null) {
|
||||
vo.setSysCreateTime(sysCreateTimestamp.toLocalDateTime());
|
||||
}
|
||||
|
||||
vo.setForUsers((String) row[12]);
|
||||
vo.setStatus((Integer) row[13]);
|
||||
vo.setPublished((Boolean) row[14]);
|
||||
vo.setForUsers((String) row[13]);
|
||||
vo.setStatus((Integer) row[14]);
|
||||
vo.setPublished((Boolean) row[15]);
|
||||
|
||||
// 增加对Timestamp和LocalDateTime的兼容性,防止Timestamp为null的情况
|
||||
Timestamp publishTimestamp = (Timestamp) row[15];
|
||||
Timestamp publishTimestamp = (Timestamp) row[16];
|
||||
if (publishTimestamp != null) {
|
||||
vo.setPublishTime(publishTimestamp.toLocalDateTime());
|
||||
}
|
||||
|
||||
// 防止Number为null的情况
|
||||
Number studysNum = (Number) row[16];
|
||||
Number studysNum = (Number) row[17];
|
||||
if (studysNum != null) {
|
||||
vo.setStudys(studysNum.intValue());
|
||||
} else {
|
||||
vo.setStudys(0);
|
||||
}
|
||||
|
||||
Number scoreNum = (Number) row[17];
|
||||
Number scoreNum = (Number) row[18];
|
||||
if (scoreNum != null) {
|
||||
vo.setScore(scoreNum.floatValue());
|
||||
} else {
|
||||
vo.setScore(0.0f);
|
||||
}
|
||||
|
||||
Number durationNum = (Number) row[18];
|
||||
Number durationNum = (Number) row[19];
|
||||
if (durationNum != null) {
|
||||
vo.setCourseDuration(durationNum.longValue());
|
||||
} else {
|
||||
vo.setCourseDuration(0L);
|
||||
}
|
||||
|
||||
vo.setEnabled((Boolean) row[19]);
|
||||
vo.setOpenCourse((Integer) row[20]);
|
||||
vo.setIsTop((Boolean) row[21]);
|
||||
vo.setTeacherName((String) row[22]);
|
||||
vo.setSortWeight((Integer) row[23]);
|
||||
vo.setEnabled((Boolean) row[20]);
|
||||
vo.setOpenCourse((Integer) row[21]);
|
||||
vo.setIsTop((Boolean) row[22]);
|
||||
vo.setTeacherName((String) row[23]);
|
||||
vo.setSortWeight((Integer) row[24]);
|
||||
// 增加对Timestamp和LocalDateTime的兼容性,防止Timestamp为null的情况
|
||||
Timestamp sysUpdateTimestamp = (Timestamp) row[25];
|
||||
if (sysUpdateTimestamp != null) {
|
||||
vo.setSysUpdateTime(sysUpdateTimestamp.toLocalDateTime());
|
||||
}
|
||||
coursePageVos.add(vo);
|
||||
}
|
||||
return coursePageVos;
|
||||
@@ -229,7 +279,7 @@ public class CourseDao extends BaseDao<Course> {
|
||||
// 开头判断:课程培训时间的两个参数是否不为null
|
||||
boolean filterLearningTime = queryDTO.getLearningTimeStart() != null && queryDTO.getLearningTimeEnd() != null;
|
||||
builder.append(" FROM boe_course c");
|
||||
// 聚合教师姓名(仅未删除的教师)
|
||||
// 聚合教师姓名
|
||||
builder.append(System.lineSeparator());
|
||||
builder.append("LEFT JOIN (SELECT course_id, GROUP_CONCAT(teacher_name ORDER BY id SEPARATOR ',') AS teacher_names FROM boe_course_teacher GROUP BY course_id) tch ON c.id = tch.course_id");
|
||||
// 学习人数聚合(满足时间条件的学习记录,且学习记录有效)
|
||||
@@ -253,6 +303,9 @@ public class CourseDao extends BaseDao<Course> {
|
||||
builder.append(System.lineSeparator());
|
||||
builder.append("LEFT JOIN ").append(userCenterSchema)
|
||||
.append(".organization org ON c.org_id = org.organization_id AND org.deleted = 0");
|
||||
// 教师联查
|
||||
builder.append(System.lineSeparator());
|
||||
builder.append("LEFT JOIN boe_course_teacher ct ON c.id = ct.course_id");
|
||||
|
||||
// where条件
|
||||
// 第一个条件:deleted = 0
|
||||
@@ -302,9 +355,14 @@ public class CourseDao extends BaseDao<Course> {
|
||||
builder.append("AND (:learningTimeStart IS NULL OR :learningTimeEnd IS NULL OR EXISTS (SELECT 1 FROM boe_study_course sc WHERE sc.course_id = c.id AND sc.finish_time IS NOT NULL AND sc.add_time >= :learningTimeStart AND sc.finish_time <= :learningTimeEnd))");
|
||||
}
|
||||
// 授课教师
|
||||
if (StringUtils.isNotBlank(queryDTO.getTeacherName())) {
|
||||
if (StringUtils.isNotBlank(queryDTO.getTeacherId())) {
|
||||
builder.append(System.lineSeparator());
|
||||
builder.append("AND tch.teacher_names LIKE CONCAT('%', :teacherName, '%')");
|
||||
// 判断teacherId是一个还是多个
|
||||
if (queryDTO.getTeacherId().contains(",")) {
|
||||
builder.append("AND ct.teacher_id IN (:teacherIdList)");
|
||||
} else {
|
||||
builder.append("AND ct.teacher_id = :teacherId");
|
||||
}
|
||||
}
|
||||
// 展开后条件
|
||||
if (queryDTO.getEnabled() != null) {
|
||||
@@ -319,9 +377,13 @@ public class CourseDao extends BaseDao<Course> {
|
||||
builder.append(System.lineSeparator());
|
||||
builder.append("AND c.org_id = :orgId");
|
||||
}
|
||||
if (StringUtils.isNotBlank(queryDTO.getCreateUser())) {
|
||||
if (StringUtils.isNotBlank(queryDTO.getCreateUserId())) {
|
||||
builder.append(System.lineSeparator());
|
||||
builder.append("AND c.sys_create_by LIKE CONCAT('%', :createUser, '%')");
|
||||
if (queryDTO.getCreateUserId().contains(",")) {
|
||||
builder.append("AND c.sys_create_aid IN (:createUserIdList)");
|
||||
} else {
|
||||
builder.append("AND c.sys_create_aid = :createUserId");
|
||||
}
|
||||
}
|
||||
if (StringUtils.isNotBlank(queryDTO.getCreateFrom())) {
|
||||
builder.append(System.lineSeparator());
|
||||
@@ -402,8 +464,14 @@ public class CourseDao extends BaseDao<Course> {
|
||||
query.setParameter("learningTimeStart", queryDTO.getLearningTimeStart());
|
||||
query.setParameter("learningTimeEnd", queryDTO.getLearningTimeEnd());
|
||||
}
|
||||
if (StringUtils.isNotBlank(queryDTO.getTeacherName())) {
|
||||
query.setParameter("teacherName", queryDTO.getTeacherName());
|
||||
if (StringUtils.isNotBlank(queryDTO.getTeacherId())) {
|
||||
String teacherIdStr = queryDTO.getTeacherId();
|
||||
if (teacherIdStr.contains(",")) {
|
||||
List<String> teacherIdList = Arrays.asList(teacherIdStr.split(","));
|
||||
query.setParameter("teacherIdList", teacherIdList);
|
||||
} else {
|
||||
query.setParameter("teacherId", queryDTO.getTeacherId());
|
||||
}
|
||||
}
|
||||
if (queryDTO.getEnabled() != null) {
|
||||
query.setParameter("enabled", queryDTO.getEnabled());
|
||||
@@ -414,8 +482,14 @@ public class CourseDao extends BaseDao<Course> {
|
||||
if (StringUtils.isNotBlank(queryDTO.getResOwner1())) {
|
||||
query.setParameter("orgId", queryDTO.getResOwner1());
|
||||
}
|
||||
if (StringUtils.isNotBlank(queryDTO.getCreateUser())) {
|
||||
query.setParameter("createUser", queryDTO.getCreateUser());
|
||||
if (StringUtils.isNotBlank(queryDTO.getCreateUserId())) {
|
||||
String createUserIdStr = queryDTO.getCreateUserId();
|
||||
if (createUserIdStr.contains(",")) {
|
||||
List<String> createUserIdList = Arrays.asList(createUserIdStr.split(","));
|
||||
query.setParameter("createUserIdList", createUserIdList);
|
||||
} else {
|
||||
query.setParameter("createUserId", queryDTO.getCreateUserId());
|
||||
}
|
||||
}
|
||||
if (StringUtils.isNotBlank(queryDTO.getCreateFrom())) {
|
||||
query.setParameter("createFrom", queryDTO.getCreateFrom());
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.xboe.module.course.dao;
|
||||
|
||||
import com.xboe.module.course.dto.BPMCallbackDto;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import com.xboe.core.orm.BaseDao;
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.xboe.module.course.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 25.12.12新增,BPM回调接口入参
|
||||
*
|
||||
*/
|
||||
@Data
|
||||
public class BPMCallbackDto {
|
||||
/**
|
||||
* 审批流程ID
|
||||
*/
|
||||
private String auditId;
|
||||
/**
|
||||
* 审核结果,可选值:`success` (通过), `reject` (驳回)
|
||||
*/
|
||||
private String auditResult;
|
||||
/**
|
||||
* 最终审核意见
|
||||
* auditResult为reject时,传最后驳回时的驳回意见
|
||||
*/
|
||||
private String auditComment;
|
||||
/**
|
||||
* 最终审核人
|
||||
* auditResult为reject时,传最后驳回的用户
|
||||
*/
|
||||
private String lastAuditUser;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.xboe.module.course.dto;
|
||||
|
||||
import lombok.Data;
|
||||
/**
|
||||
* 25.12.10新增,提交审核到BPM时的返回结果
|
||||
*
|
||||
*/
|
||||
@Data
|
||||
public class BPMResponseDto {
|
||||
/**
|
||||
* 调用BPM返回的状态:success/error
|
||||
*/
|
||||
private String status;
|
||||
/**
|
||||
* 调用BPM成功时返回的审批单ID
|
||||
*/
|
||||
private String auditId;
|
||||
/**
|
||||
* 调用BPM成功时返回的审批人姓名
|
||||
*/
|
||||
private String auditApprover;
|
||||
/**
|
||||
* 调用BPM失败时返回的错误信息
|
||||
*/
|
||||
private String message;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.xboe.module.course.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 批量上传响应 DTO
|
||||
*/
|
||||
@Data
|
||||
public class BatchUploadResponseDto {
|
||||
private boolean success;
|
||||
private String message;
|
||||
private int totalCount;
|
||||
private int successCount;
|
||||
private int failCount;
|
||||
private List<UploadResult> results;
|
||||
private String courseId;
|
||||
|
||||
@Data
|
||||
public static class UploadResult {
|
||||
private int itemIndex; // 标识的是第几个上传项
|
||||
private String contentName;
|
||||
private Integer contentType;
|
||||
private Integer resourceType;
|
||||
private boolean success;
|
||||
private String message;
|
||||
private String contentId;
|
||||
private List<ResourceResult> resources;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class ResourceResult {
|
||||
private String resourceName;
|
||||
private boolean success;
|
||||
private String message;
|
||||
private String fileId;
|
||||
private String fileUrl;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
package com.xboe.module.course.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 批量上传DTO(支持9种资源:视频、音频、文档、图文、SCORM包、外部链接、作业、考试、评估)
|
||||
*/
|
||||
@Data
|
||||
public class BatchUploadWithNullDto {
|
||||
|
||||
@NotBlank(message = "课程ID不能为空")
|
||||
private String courseId;
|
||||
|
||||
@NotNull(message = "内容项列表不能为空")
|
||||
private List<ContentItem> contentItems;
|
||||
|
||||
/**
|
||||
* 资源归属信息(课件资源使用)
|
||||
*/
|
||||
private ResourceOwnerInfo ownerInfo;
|
||||
|
||||
@NotNull(message = "操作类型不能为空")
|
||||
private Integer operationType; // 1新增
|
||||
|
||||
/**
|
||||
* 内容项DTO(支持9种资源)
|
||||
*/
|
||||
@Data
|
||||
public static class ContentItem {
|
||||
/**
|
||||
* 内容ID
|
||||
*/
|
||||
private String contentId;
|
||||
|
||||
|
||||
@Size(max = 200, message = "内容名称不能超过200字")
|
||||
private String contentName;
|
||||
|
||||
/**
|
||||
* 内容类型:
|
||||
* 1: 课件(包含6种资源类型)
|
||||
* 2: 作业
|
||||
* 3: 考试
|
||||
* 4: 评估
|
||||
*/
|
||||
private Integer contentType;
|
||||
|
||||
/**
|
||||
* 排序索引
|
||||
*/
|
||||
private Integer sortIndex;
|
||||
|
||||
/**
|
||||
* 课时(分钟)
|
||||
*/
|
||||
private Integer duration;
|
||||
|
||||
/**
|
||||
* 是否删除
|
||||
*/
|
||||
private Boolean deleted;
|
||||
|
||||
/**
|
||||
* 章节ID(关联到特定的小节)
|
||||
*/
|
||||
private String csectionId;
|
||||
|
||||
// ---------- 课件相关字段 ----------
|
||||
/**
|
||||
* 资源类型(仅课件使用):
|
||||
* 10: 视频
|
||||
* 20: 音频
|
||||
* 40: 文档
|
||||
* 41: 图文
|
||||
* 50: SCORM包
|
||||
* 90: 外部链接
|
||||
*/
|
||||
private Integer resourceType;
|
||||
|
||||
/**
|
||||
* 文件资源信息
|
||||
*/
|
||||
private FileResourceInfo fileResource;
|
||||
|
||||
/**
|
||||
* 图文资源信息
|
||||
*/
|
||||
private GraphicTextResourceInfo graphicTextResource;
|
||||
|
||||
/**
|
||||
* 外部链接资源信息
|
||||
*/
|
||||
private ExternalLinkResourceInfo externalLinkResource;
|
||||
|
||||
/**
|
||||
* 作业相关字段
|
||||
*/
|
||||
private HomeworkInfo homeworkInfo;
|
||||
|
||||
/**
|
||||
* 考试相关字段
|
||||
*/
|
||||
private ExamInfo examInfo;
|
||||
|
||||
/**
|
||||
* 评估相关字段
|
||||
*/
|
||||
private AssessInfo assessInfo;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件资源信息
|
||||
*/
|
||||
@Data
|
||||
public static class FileResourceInfo {
|
||||
private String fileBase64;
|
||||
private String originalFileName;
|
||||
private Boolean down;
|
||||
private String remark;
|
||||
private Integer device;
|
||||
private String decoder;
|
||||
private Integer videoWidth;
|
||||
private Integer videoHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* 图文资源信息
|
||||
*/
|
||||
@Data
|
||||
public static class GraphicTextResourceInfo {
|
||||
private String title;
|
||||
private String content;
|
||||
private String imageBase64;
|
||||
private String imageName;
|
||||
private TextFormat format;
|
||||
private String fontSize;
|
||||
private String lineHeight;
|
||||
private String textAlign;
|
||||
}
|
||||
|
||||
/**
|
||||
* 外部链接资源信息
|
||||
*/
|
||||
@Data
|
||||
public static class ExternalLinkResourceInfo {
|
||||
private String url;
|
||||
private Integer openType;
|
||||
private String title;
|
||||
private String description;
|
||||
}
|
||||
|
||||
/**
|
||||
* 作业信息
|
||||
*/
|
||||
@Data
|
||||
public static class HomeworkInfo {
|
||||
private String name;
|
||||
private String content;
|
||||
private String file;
|
||||
private LocalDateTime deadTime;
|
||||
private Integer submitMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 考试信息
|
||||
*/
|
||||
@Data
|
||||
public static class ExamInfo {
|
||||
private String testName;
|
||||
private Integer testDuration;
|
||||
private Boolean showAnalysis;
|
||||
private Boolean showAnswer;
|
||||
private Integer times;
|
||||
private Integer arrange;
|
||||
private Integer scoringType;
|
||||
private Integer passLine;
|
||||
private Boolean randomMode;
|
||||
private Integer qnum;
|
||||
private Float qscore;
|
||||
private Integer paperType;
|
||||
private Boolean percentScore;
|
||||
private String paperId;
|
||||
private String paperContent;
|
||||
private String info;
|
||||
}
|
||||
|
||||
/**
|
||||
* 评估信息
|
||||
*/
|
||||
@Data
|
||||
public static class AssessInfo {
|
||||
private String assessId;
|
||||
private String question;
|
||||
private Integer qType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 资源归属信息
|
||||
*/
|
||||
@Data
|
||||
public static class ResourceOwnerInfo {
|
||||
private String orgId;
|
||||
private String orgName;
|
||||
private String resOwner1;
|
||||
private String resOwner2;
|
||||
private String resOwner3;
|
||||
private String ownership1;
|
||||
private String ownership2;
|
||||
private String ownership3;
|
||||
}
|
||||
|
||||
/**
|
||||
* 文本格式
|
||||
*/
|
||||
@Data
|
||||
public static class TextFormat {
|
||||
private Boolean bold;
|
||||
private Boolean italic;
|
||||
private Boolean underline;
|
||||
private Boolean strikethrough;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证整个DTO
|
||||
*/
|
||||
public List<String> validate() {
|
||||
List<String> errors = new ArrayList<>();
|
||||
|
||||
if (StringUtils.isBlank(courseId)) {
|
||||
errors.add("课程ID不能为空");
|
||||
}
|
||||
|
||||
if (operationType == null) {
|
||||
errors.add("操作类型不能为空");
|
||||
} else if (operationType != 1 ) {
|
||||
errors.add("操作类型必须是1(新增)");
|
||||
}
|
||||
|
||||
if (CollectionUtils.isEmpty(contentItems)) {
|
||||
errors.add("内容项列表不能为空");
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,20 @@ import lombok.Data;
|
||||
*/
|
||||
@Data
|
||||
public class CourseFullDto {
|
||||
/**
|
||||
* 25.12.10新增
|
||||
* 审核类型
|
||||
* 0: 停用审核
|
||||
* 1: 创建审核
|
||||
* 2: 编辑审核
|
||||
* 3:启用审核
|
||||
*/
|
||||
private Integer auditType;
|
||||
/**
|
||||
* 25.12.10新增
|
||||
* 提交审核的用户id
|
||||
*/
|
||||
private String userId;
|
||||
|
||||
/**是否是新建课程*/
|
||||
private Boolean isNew;
|
||||
|
||||
@@ -31,6 +31,11 @@ public class CoursePageQueryDTO {
|
||||
/**创建人*/
|
||||
private String createUser;
|
||||
|
||||
/**
|
||||
* 创建人id
|
||||
*/
|
||||
private String createUserId;
|
||||
|
||||
/**课程分类的一级*/
|
||||
private String sysType1;
|
||||
|
||||
@@ -43,6 +48,11 @@ public class CoursePageQueryDTO {
|
||||
/**授课教师*/
|
||||
private String teacherName;
|
||||
|
||||
/**
|
||||
* 授课教师id
|
||||
*/
|
||||
private String teacherId;
|
||||
|
||||
/**培训时间筛选类型*/
|
||||
private String learningTimeType;
|
||||
|
||||
@@ -77,4 +87,11 @@ public class CoursePageQueryDTO {
|
||||
|
||||
/**排序顺序*/
|
||||
private Boolean orderAsc;
|
||||
|
||||
/**
|
||||
* 是否是新建在线课程页面
|
||||
* false时,代表是在管理端的“在线管理”、“我开发的课程”
|
||||
* true时,代表是在学习路径图/项目管理 中,创建在线课程的选择页面
|
||||
*/
|
||||
private Boolean isCreateCourse;
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package com.xboe.module.course.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 课程资源上传 DTO(用于单个课件资源处理)
|
||||
*/
|
||||
@Data
|
||||
public class CourseResourceUploadDto {
|
||||
|
||||
@NotBlank(message = "课程ID不能为空")
|
||||
private String courseId;
|
||||
|
||||
@NotNull(message = "资源列表不能为空")
|
||||
private List<CourseFileResource> resources;
|
||||
|
||||
/**
|
||||
* 资源归属信息
|
||||
*/
|
||||
private ResourceOwnerInfo ownerInfo;
|
||||
|
||||
/**
|
||||
* 资源归属信息DTO
|
||||
*/
|
||||
@Data
|
||||
public static class ResourceOwnerInfo {
|
||||
private String orgId;
|
||||
private String orgName;
|
||||
private String resOwner1;
|
||||
private String resOwner2;
|
||||
private String resOwner3;
|
||||
private String ownership1;
|
||||
private String ownership2;
|
||||
private String ownership3;
|
||||
}
|
||||
|
||||
/**
|
||||
* 课程文件资源
|
||||
*/
|
||||
@Data
|
||||
public static class CourseFileResource {
|
||||
/**
|
||||
* 资源类型:
|
||||
* 10: 视频
|
||||
* 20: 音频
|
||||
* 40: 文档
|
||||
* 41: 图文
|
||||
* 50: SCORM包
|
||||
* 90: 外部链接
|
||||
*/
|
||||
@NotNull(message = "资源类型不能为空")
|
||||
private Integer resType;
|
||||
|
||||
@NotBlank(message = "资源名称不能为空")
|
||||
@Size(max = 100, message = "资源名称不能超过100字")
|
||||
private String name;
|
||||
|
||||
private String fileName;
|
||||
private String fileType;
|
||||
private Integer fileSize;
|
||||
private String filePath;
|
||||
private String previewFilePath;
|
||||
private Integer duration;
|
||||
private Boolean down = false;
|
||||
|
||||
@Size(max = 200, message = "备注不能超过200字")
|
||||
private String remark;
|
||||
|
||||
private Integer device;
|
||||
private String fileBase64;
|
||||
private String originalFileName;
|
||||
private GraphicTextContent graphicText;
|
||||
private ExternalLinkContent externalLink;
|
||||
private String decoder;
|
||||
private Integer videoWidth;
|
||||
private Integer videoHeight;
|
||||
private String ownership1;
|
||||
private String ownership2;
|
||||
private String ownership3;
|
||||
private Integer converStatus = 0;
|
||||
private String converError;
|
||||
}
|
||||
|
||||
/**
|
||||
* 图文内容DTO
|
||||
*/
|
||||
@Data
|
||||
public static class GraphicTextContent {
|
||||
@NotBlank(message = "图文标题不能为空")
|
||||
@Size(max = 100, message = "图文标题不能超过100字")
|
||||
private String title;
|
||||
|
||||
@NotBlank(message = "图文内容不能为空")
|
||||
private String content;
|
||||
|
||||
private String imageBase64;
|
||||
private String imageName;
|
||||
private TextFormat format;
|
||||
private String fontSize;
|
||||
private String lineHeight;
|
||||
private String textAlign;
|
||||
}
|
||||
|
||||
/**
|
||||
* 外部链接内容DTO
|
||||
*/
|
||||
@Data
|
||||
public static class ExternalLinkContent {
|
||||
@NotBlank(message = "链接地址不能为空")
|
||||
private String url;
|
||||
|
||||
private Integer openType = 2;
|
||||
private String title;
|
||||
private String description;
|
||||
}
|
||||
|
||||
/**
|
||||
* 文本格式DTO
|
||||
*/
|
||||
@Data
|
||||
public static class TextFormat {
|
||||
private Boolean bold = false;
|
||||
private Boolean italic = false;
|
||||
private Boolean underline = false;
|
||||
private Boolean strikethrough = false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package com.xboe.module.course.dto;
|
||||
|
||||
import com.xboe.module.course.entity.CourseCrowd;
|
||||
import com.xboe.module.course.entity.CourseTeacher;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 课程课件保存/编辑 DTO
|
||||
*/
|
||||
@Data
|
||||
public class CourseWareSaveDto {
|
||||
|
||||
@NotNull(message = "课程基础信息不能为空")
|
||||
private CourseInfo course;
|
||||
|
||||
|
||||
@NotNull(message = "至少选择一位授课教师")
|
||||
private List<CourseTeacher> teacherList;
|
||||
|
||||
|
||||
private List<CourseCrowd> crowdList;
|
||||
|
||||
/**
|
||||
* 课程分类(三级)
|
||||
*/
|
||||
@Data
|
||||
public static class TypeLevelInfo {
|
||||
@NotBlank(message = "分类ID不能为空")
|
||||
@Size(max = 20, message = "分类ID长度不能超过20位")
|
||||
private String id;
|
||||
|
||||
@Size(max = 20, message = "分类名称长度不能超过20位")
|
||||
private String name;
|
||||
|
||||
private String parentId;
|
||||
}
|
||||
|
||||
@NotNull(message = "课程一级分类不能为空")
|
||||
private TypeLevelInfo sysType1;
|
||||
|
||||
private TypeLevelInfo sysType2;
|
||||
|
||||
private TypeLevelInfo sysType3;
|
||||
|
||||
/**
|
||||
* 资源归属(三级)
|
||||
*/
|
||||
@Data
|
||||
public static class OrgLevelInfo {
|
||||
@NotBlank(message = "组织ID不能为空")
|
||||
@Size(max = 20, message = "组织ID长度不能超过20位")
|
||||
private String id;
|
||||
|
||||
@Size(max = 50, message = "组织名称长度不能超过50位")
|
||||
private String name;
|
||||
|
||||
private String parentId;
|
||||
}
|
||||
|
||||
@NotNull(message = "资源一级归属不能为空")
|
||||
private OrgLevelInfo resOwner1;
|
||||
|
||||
private OrgLevelInfo resOwner2;
|
||||
|
||||
private OrgLevelInfo resOwner3;
|
||||
|
||||
@NotNull(message = "观看设置不能为空")
|
||||
private Integer device;
|
||||
|
||||
/**
|
||||
* 课程信息DTO
|
||||
*/
|
||||
@Data
|
||||
public static class CourseInfo {
|
||||
private String id;
|
||||
private Integer sysVersion;
|
||||
|
||||
@NotBlank(message = "课程名称不能为空")
|
||||
@Size(max = 100, message = "课程名称不能超过100字")
|
||||
private String name;
|
||||
|
||||
@Size(max = 200, message = "课程价值不能超过200字")
|
||||
private String value;
|
||||
|
||||
@Size(max = 500, message = "课程简介不能超过500字")
|
||||
private String summary;
|
||||
|
||||
@Size(max = 50, message = "目标人群不能超过50字")
|
||||
private String forUsers;
|
||||
|
||||
@Size(max = 200, message = "课程标签不能超过200字")
|
||||
private String tags;
|
||||
|
||||
private String coverImg;
|
||||
private Integer type;
|
||||
private String orgId;
|
||||
private Boolean enabled = true;
|
||||
private Boolean isTop = false;
|
||||
private Integer studyTime;
|
||||
private String overview;
|
||||
private Integer openCourse;
|
||||
private Boolean visible = true;
|
||||
private Boolean orderStudy = false;
|
||||
private String keywords;
|
||||
private String forScene;
|
||||
private String createFrom;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.xboe.module.course.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 层级结构课程DTO
|
||||
*/
|
||||
@Data
|
||||
public class HierarchicalCourseDto {
|
||||
|
||||
@NotBlank(message = "课程ID不能为空")
|
||||
private String courseId;
|
||||
|
||||
@NotNull(message = "操作类型不能为空")
|
||||
private Integer operationType;
|
||||
|
||||
private BatchUploadWithNullDto.ResourceOwnerInfo ownerInfo;
|
||||
|
||||
@NotEmpty(message = "章列表不能为空")
|
||||
private List<Chapter> chapters;
|
||||
|
||||
@Data
|
||||
public static class Chapter {
|
||||
|
||||
@NotBlank(message = "章ID不能为空")
|
||||
private String chapterId;
|
||||
|
||||
private String chapterName;
|
||||
|
||||
@NotEmpty(message = "节列表不能为空")
|
||||
private List<Section> sections;
|
||||
}
|
||||
|
||||
/**
|
||||
* 节信息
|
||||
*/
|
||||
@Data
|
||||
public static class Section {
|
||||
|
||||
@NotBlank(message = "节ID不能为空")
|
||||
private String sectionId;
|
||||
|
||||
private String sectionName;
|
||||
|
||||
@NotNull(message = "内容项不能为空")
|
||||
private BatchUploadWithNullDto.ContentItem content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.xboe.module.course.dto.content;
|
||||
|
||||
import com.xboe.standard.enums.BoedxContentType;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 课程内容DTO
|
||||
*/
|
||||
@Data
|
||||
public class CourseContentDTO {
|
||||
/**
|
||||
* 内容ID
|
||||
*/
|
||||
private String contentId;
|
||||
|
||||
/**
|
||||
* 内容名称
|
||||
*/
|
||||
private String contentName;
|
||||
|
||||
/**
|
||||
* 内容类型
|
||||
* @see BoedxContentType
|
||||
* 有效的类型:10-视频
|
||||
* 20-音频
|
||||
* 40-文档
|
||||
* 41-图文(Html)
|
||||
* 52-链接
|
||||
* 50-SCORM
|
||||
* 60-作业
|
||||
* 61-考试
|
||||
* 62-评估
|
||||
*/
|
||||
private Integer contentType;
|
||||
|
||||
/**
|
||||
* 排序索引
|
||||
*/
|
||||
private Integer sortIndex;
|
||||
|
||||
/**
|
||||
* 课时(分钟)
|
||||
*/
|
||||
private Integer duration;
|
||||
|
||||
/**
|
||||
* 具体课件内容
|
||||
* 图文会用到
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 是否使用新上传的课件
|
||||
* 0-否
|
||||
* 1-是
|
||||
*/
|
||||
private Integer isNewFile;
|
||||
|
||||
/**
|
||||
* 课件内容引用ID
|
||||
* 当课件不是新上传,而是选择已有的情况下,用这个字段
|
||||
*/
|
||||
private String contentRefId;
|
||||
|
||||
/**
|
||||
* 文件相关信息
|
||||
*/
|
||||
private FileResourceInfoDTO file;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.xboe.module.course.dto.content;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 课程内容DTO
|
||||
* 涉及一个课程中所有的课程内容,结构是章节结构
|
||||
*/
|
||||
@Data
|
||||
public class CourseFullContentDTO {
|
||||
|
||||
/**
|
||||
* 课程id
|
||||
*/
|
||||
private String courseId;
|
||||
|
||||
/**
|
||||
* 章列表
|
||||
*/
|
||||
private List<CourseSectionDTO> sections;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.xboe.module.course.dto.content;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 课程章DTO
|
||||
*/
|
||||
@Data
|
||||
public class CourseSectionDTO {
|
||||
|
||||
/**
|
||||
* 章id
|
||||
*/
|
||||
private String sectionId;
|
||||
|
||||
/**
|
||||
* 章名称
|
||||
*/
|
||||
private String sectionName;
|
||||
|
||||
/**
|
||||
* 章里面的课件内容
|
||||
*/
|
||||
private List<CourseContentDTO> contents;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.xboe.module.course.dto.content;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 课程内容对应的文件资源
|
||||
*/
|
||||
@Data
|
||||
public class FileResourceInfoDTO {
|
||||
|
||||
/**
|
||||
* 文件名称
|
||||
*/
|
||||
private String fileName;
|
||||
|
||||
/**
|
||||
* 文件类型
|
||||
*/
|
||||
private String fileType;
|
||||
|
||||
/**
|
||||
* 文件路径
|
||||
*/
|
||||
private String filePath;
|
||||
}
|
||||
@@ -126,7 +126,7 @@ public class Course extends BaseEntity {
|
||||
/**
|
||||
* 课程名称
|
||||
* */
|
||||
@Column(name = "name",nullable=false, length = 100)
|
||||
@Column(name = "name",nullable=false, length = 50)
|
||||
private String name;
|
||||
|
||||
@Column(name = "fulltext_id",length = 36)
|
||||
@@ -409,13 +409,15 @@ public class Course extends BaseEntity {
|
||||
/**
|
||||
* 课程时长(秒)
|
||||
*/
|
||||
@Column(name = "course_duration")
|
||||
// @Column(name = "course_duration")
|
||||
@Transient
|
||||
private Long courseDuration;
|
||||
|
||||
/**
|
||||
* 排序权重
|
||||
*/
|
||||
@Column(name = "sort_weight")
|
||||
// @Column(name = "sort_weight")
|
||||
@Transient
|
||||
private Integer sortWeight;
|
||||
|
||||
/**
|
||||
@@ -423,7 +425,8 @@ public class Course extends BaseEntity {
|
||||
* teacher-教师端
|
||||
* admin-管理员端
|
||||
*/
|
||||
@Column(name = "create_from")
|
||||
// @Column(name = "create_from")
|
||||
@Transient
|
||||
private String createFrom;
|
||||
|
||||
public Course(String id,String name,String summary,String coverImg,String sysCreateAid,String sysCreateBy,Integer type,LocalDateTime publishTime){
|
||||
|
||||
@@ -141,6 +141,28 @@ public class CourseHRBPAudit extends IdEntity {
|
||||
@Column(name = "last_remark")
|
||||
private String lastRemark;
|
||||
|
||||
/**
|
||||
* 25.12.15新增
|
||||
* 审核类型
|
||||
*/
|
||||
@Column(name = "audit_type")
|
||||
private Integer auditType;
|
||||
|
||||
/**
|
||||
* 25.12.15新增
|
||||
* 审核结果
|
||||
* 1通过,0驳回
|
||||
*/
|
||||
@Column(name = "audit_result")
|
||||
private Integer auditResult;
|
||||
|
||||
/**
|
||||
* 25.12.15新增
|
||||
* 审核流程Id
|
||||
*/
|
||||
@Column(name = "audit_id")
|
||||
private String auditId;
|
||||
|
||||
@Transient
|
||||
private String courseName;
|
||||
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.xboe.module.course.service;
|
||||
|
||||
import com.xboe.module.course.dto.BatchUploadResponseDto;
|
||||
import com.xboe.module.course.dto.BatchUploadWithNullDto;
|
||||
import com.xboe.module.course.dto.CourseResourceUploadDto;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 课程内容验证 服务接口
|
||||
*/
|
||||
public interface CourseContentValidationService {
|
||||
|
||||
/**
|
||||
* 验证批量上传DTO
|
||||
*/
|
||||
List<String> validateBatchUpload(BatchUploadWithNullDto dto);
|
||||
|
||||
/**
|
||||
* 验证内容项
|
||||
*/
|
||||
List<String> validateContentItem(BatchUploadWithNullDto.ContentItem item, Integer operationType, int index);
|
||||
|
||||
/**
|
||||
* 验证资源上传DTO
|
||||
*/
|
||||
String validateResource(CourseResourceUploadDto.CourseFileResource resource);
|
||||
|
||||
/**
|
||||
* 验证资源类型是否有效
|
||||
*/
|
||||
boolean isValidResourceType(Integer resType);
|
||||
|
||||
/**
|
||||
* 验证文件扩展名
|
||||
*/
|
||||
boolean isValidFileExtension(Integer resType, String fileName);
|
||||
|
||||
/**
|
||||
* 获取文件扩展名
|
||||
*/
|
||||
String getFileExtension(String filename);
|
||||
|
||||
/**
|
||||
* 获取资源类型名称
|
||||
*/
|
||||
String getResourceTypeName(Integer resType);
|
||||
|
||||
/**
|
||||
* 创建一个新的批量上传响应DTO
|
||||
* @param courseId 课程ID
|
||||
* @param totalCount 总数
|
||||
* @return BatchUploadResponseDto
|
||||
*/
|
||||
BatchUploadResponseDto createBatchUploadResponse(String courseId, int totalCount);
|
||||
|
||||
/**
|
||||
* 添加结果到批量上传响应DTO
|
||||
* @param response 响应DTO
|
||||
* @param result 结果
|
||||
*/
|
||||
void addResultToBatchUploadResponse(BatchUploadResponseDto response, BatchUploadResponseDto.UploadResult result);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.xboe.module.course.service;
|
||||
|
||||
import com.xboe.module.course.dto.CourseResourceUploadDto;
|
||||
import com.xboe.module.course.entity.CourseFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 课程资源服务接口
|
||||
*/
|
||||
public interface CourseResourceService {
|
||||
|
||||
/**
|
||||
* 处理课程资源(核心方法)
|
||||
*
|
||||
* @param courseId 课程ID
|
||||
* @param resources 资源列表
|
||||
* @param orgId 组织ID
|
||||
* @param orgName 组织名称
|
||||
* @param resOwner1 资源归属1级
|
||||
* @param resOwner2 资源归属2级
|
||||
* @param resOwner3 资源归属3级
|
||||
* @param ownership1 所有权1级
|
||||
* @param ownership2 所有权2级
|
||||
* @param ownership3 所有权3级
|
||||
*/
|
||||
void processCourseResources(String courseId,
|
||||
List<CourseResourceUploadDto.CourseFileResource> resources,
|
||||
String orgId, String orgName,
|
||||
String resOwner1, String resOwner2, String resOwner3,
|
||||
String ownership1, String ownership2, String ownership3);
|
||||
|
||||
/**
|
||||
* 获取课程资源列表
|
||||
*
|
||||
* @param courseId 课程ID
|
||||
* @return 资源列表
|
||||
*/
|
||||
List<CourseFile> getCourseResources(String courseId);
|
||||
|
||||
/**
|
||||
* 删除课程资源
|
||||
*
|
||||
* @param courseId 课程ID
|
||||
*/
|
||||
void deleteCourseResources(String courseId);
|
||||
|
||||
/**
|
||||
* 删除单个资源
|
||||
*
|
||||
* @param resourceId 资源ID
|
||||
*/
|
||||
void deleteResource(String resourceId);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.xboe.module.course.service;
|
||||
|
||||
import com.xboe.module.course.dto.BatchUploadWithNullDto;
|
||||
import com.xboe.module.course.dto.BatchUploadResponseDto;
|
||||
|
||||
/**
|
||||
* 原子批量上传服务接口
|
||||
* 支持9种资源的上传
|
||||
*/
|
||||
public interface IAtomicBatchUploadService {
|
||||
|
||||
/**
|
||||
* 原子批量上传
|
||||
* @param uploadDto 上传数据
|
||||
* @return 上传结果
|
||||
*/
|
||||
BatchUploadResponseDto atomicBatchUpload(BatchUploadWithNullDto uploadDto);
|
||||
}
|
||||
@@ -2,16 +2,18 @@ package com.xboe.module.course.service;
|
||||
|
||||
import com.xboe.common.PageList;
|
||||
import com.xboe.module.course.dto.CourseContentDto;
|
||||
import com.xboe.module.course.dto.CourseWareSaveDto;
|
||||
import com.xboe.module.course.dto.SortItem;
|
||||
import com.xboe.module.course.entity.CourseAssess;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 课程内容,当前是分着处理,之后看是否与课程服务合并在一起
|
||||
* 课程内容
|
||||
* */
|
||||
public interface ICourseContentService{
|
||||
|
||||
@@ -21,6 +23,13 @@ public interface ICourseContentService{
|
||||
*/
|
||||
void saveOrUpdate(CourseContentDto dto);
|
||||
|
||||
/**
|
||||
* 保存更新课件
|
||||
* @param dto
|
||||
* @return
|
||||
*/
|
||||
void saveOrUpdateCourseware(CourseWareSaveDto dto);
|
||||
|
||||
|
||||
/**
|
||||
* 计算总时长
|
||||
@@ -37,13 +46,12 @@ public interface ICourseContentService{
|
||||
void updateName(String id,String name);
|
||||
|
||||
/**
|
||||
* 对于已发布过的课程 ,采用逻辑删除,对于未发布过的课程采用物理删除
|
||||
* @param id
|
||||
*/
|
||||
void delete(String id,int ctype, boolean flag);
|
||||
|
||||
/**
|
||||
* 用于检查课程是否完整,是否可以提交了,只是部分必要的字段
|
||||
* 用于检查课程是否完整,是否可以提交了
|
||||
* @param courseId
|
||||
* @return
|
||||
*/
|
||||
@@ -58,7 +66,6 @@ public interface ICourseContentService{
|
||||
|
||||
/**
|
||||
* 根据课程id、章节id得到课程所有目录(即章节,分页),顺序按orderIndex 从小到大的顺序
|
||||
* 25.11.26新增
|
||||
*
|
||||
* @param pageIndex 页码
|
||||
* @param pageSize 每页数量
|
||||
|
||||
@@ -4,6 +4,7 @@ package com.xboe.module.course.service;
|
||||
import java.util.List;
|
||||
|
||||
import com.xboe.common.PageList;
|
||||
import com.xboe.module.course.dto.BPMCallbackDto;
|
||||
import com.xboe.module.course.dto.CourseHRBPAuditDto;
|
||||
import com.xboe.module.course.entity.CourseHRBPAudit;
|
||||
|
||||
@@ -11,6 +12,10 @@ import com.xboe.module.course.entity.CourseHRBPAudit;
|
||||
* 人员课程审核
|
||||
* */
|
||||
public interface ICourseHRBPAuditService {
|
||||
/**
|
||||
* BPM回调更新
|
||||
* */
|
||||
void bpmRecallUpdate(BPMCallbackDto dto);
|
||||
|
||||
/**
|
||||
* 提交审核
|
||||
@@ -32,6 +37,14 @@ public interface ICourseHRBPAuditService {
|
||||
*/
|
||||
CourseHRBPAudit hasAuditing(String courseId);
|
||||
|
||||
/**
|
||||
* 25.12.10新增
|
||||
* 检查课程是否已经有过审核记录了
|
||||
* @param courseId
|
||||
* @return
|
||||
*/
|
||||
CourseHRBPAudit hadAuditing(String courseId);
|
||||
|
||||
/**
|
||||
* 根据课程、id获取课程的审核记录
|
||||
* @param info 审核信息
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.xboe.module.course.service;
|
||||
|
||||
import com.xboe.module.course.dto.BPMResponseDto;
|
||||
import com.xboe.module.course.dto.CourseFullDto;
|
||||
|
||||
public interface ICourseManageService {
|
||||
/**
|
||||
* 25.12.10新增,调用外部接口前,启用/停用进行数据准备和json请求体构建
|
||||
*/
|
||||
String prepareDisableAuditRequest(CourseFullDto dto);
|
||||
/**
|
||||
* 25.12.10新增,调用外部接口前,创建进行数据准备和json请求体构建
|
||||
*/
|
||||
String prepareCreateAuditRequest(CourseFullDto dto);
|
||||
/**
|
||||
* 25.12.10新增,BPM外部接口调用
|
||||
*/
|
||||
BPMResponseDto callBPMInterface(String jsonRequestBody);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.xboe.module.course.service;
|
||||
|
||||
import com.xboe.module.course.vo.TypeTreeVo;
|
||||
import com.xboe.system.organization.vo.OrganizationVo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 分类树和组织树服务接口
|
||||
*/
|
||||
public interface TypeTreeService {
|
||||
|
||||
/**
|
||||
* 获取课程分类树(三级)
|
||||
*/
|
||||
List<TypeTreeVo> getCourseTypeTree();
|
||||
|
||||
/**
|
||||
* 获取资源归属树(组织机构树)
|
||||
*/
|
||||
List<OrganizationVo> getResourceOwnerTree();
|
||||
|
||||
/**
|
||||
* 根据父级ID获取子分类
|
||||
*/
|
||||
List<TypeTreeVo> getChildTypes(String parentId);
|
||||
|
||||
/**
|
||||
* 根据父级ID获取子组织
|
||||
*/
|
||||
List<OrganizationVo> getChildOrgs(String parentId);
|
||||
|
||||
/**
|
||||
* 验证分类层级关系
|
||||
*/
|
||||
boolean validateTypeHierarchy(String childId, String parentId);
|
||||
}
|
||||
@@ -0,0 +1,707 @@
|
||||
package com.xboe.module.course.service.impl;
|
||||
|
||||
import com.xboe.core.orm.FieldFilters;
|
||||
import com.xboe.module.course.dao.*;
|
||||
import com.xboe.module.course.dto.BatchUploadWithNullDto;
|
||||
import com.xboe.module.course.dto.BatchUploadResponseDto;
|
||||
import com.xboe.module.course.dto.CourseResourceUploadDto;
|
||||
import com.xboe.module.course.entity.*;
|
||||
import com.xboe.module.course.service.CourseContentValidationService;
|
||||
import com.xboe.module.course.service.IAtomicBatchUploadService;
|
||||
import com.xboe.module.course.service.CourseResourceService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class AtomicBatchUploadServiceImpl implements IAtomicBatchUploadService {
|
||||
|
||||
@Resource
|
||||
private CourseContentDao courseContentDao;
|
||||
|
||||
@Resource
|
||||
private CourseFileDao courseFileDao;
|
||||
|
||||
@Resource
|
||||
private CourseHomeWorkDao courseHomeWorkDao;
|
||||
|
||||
@Resource
|
||||
private CourseExamDao courseExamDao;
|
||||
|
||||
@Resource
|
||||
private CourseAssessDao courseAssessDao;
|
||||
|
||||
@Resource
|
||||
private CourseResourceService courseResourceService;
|
||||
|
||||
@Resource
|
||||
private CourseContentValidationService courseContentValidationService;
|
||||
|
||||
/**
|
||||
* 原子批量上传
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public BatchUploadResponseDto atomicBatchUpload(BatchUploadWithNullDto uploadDto) {
|
||||
log.info("开始原子批量上传,课程ID: {}, 操作类型: {}, 内容项数量: {}",
|
||||
uploadDto.getCourseId(),
|
||||
uploadDto.getOperationType(),
|
||||
uploadDto.getContentItems().size());
|
||||
|
||||
// 创建响应对象
|
||||
BatchUploadResponseDto response = courseContentValidationService.createBatchUploadResponse(
|
||||
uploadDto.getCourseId(),
|
||||
uploadDto.getContentItems().size()
|
||||
);
|
||||
|
||||
try {
|
||||
// 验证输入
|
||||
List<String> validationErrors = uploadDto.validate();
|
||||
if (CollectionUtils.isNotEmpty(validationErrors)) {
|
||||
throw new RuntimeException("参数验证失败: " + String.join("; ", validationErrors));
|
||||
}
|
||||
|
||||
// 设置资源归属信息(用于课件资源)
|
||||
BatchUploadWithNullDto.ResourceOwnerInfo ownerInfo = uploadDto.getOwnerInfo();
|
||||
String orgId = ownerInfo != null ? ownerInfo.getOrgId() : null;
|
||||
String orgName = ownerInfo != null ? ownerInfo.getOrgName() : null;
|
||||
String resOwner1 = ownerInfo != null ? ownerInfo.getResOwner1() : null;
|
||||
String resOwner2 = ownerInfo != null ? ownerInfo.getResOwner2() : null;
|
||||
String resOwner3 = ownerInfo != null ? ownerInfo.getResOwner3() : null;
|
||||
String ownership1 = ownerInfo != null ? ownerInfo.getOwnership1() : null;
|
||||
String ownership2 = ownerInfo != null ? ownerInfo.getOwnership2() : null;
|
||||
String ownership3 = ownerInfo != null ? ownerInfo.getOwnership3() : null;
|
||||
|
||||
// 处理每个内容项
|
||||
for (int i = 0; i < uploadDto.getContentItems().size(); i++) {
|
||||
BatchUploadWithNullDto.ContentItem item = uploadDto.getContentItems().get(i);
|
||||
BatchUploadResponseDto.UploadResult itemResult = processContentItem(
|
||||
item, i, uploadDto.getOperationType(), uploadDto.getCourseId(),
|
||||
item.getCsectionId(), orgId, orgName, resOwner1, resOwner2,
|
||||
resOwner3, ownership1, ownership2, ownership3
|
||||
);
|
||||
|
||||
courseContentValidationService.addResultToBatchUploadResponse(response, itemResult);
|
||||
|
||||
// 如果有失败项,抛出异常触发回滚
|
||||
if (!itemResult.isSuccess()) {
|
||||
throw new RuntimeException("第" + (i + 1) + "项处理失败: " + itemResult.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
log.info("原子批量上传成功,课程ID: {}, 成功: {}",
|
||||
uploadDto.getCourseId(), response.getSuccessCount());
|
||||
|
||||
return response;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("原子批量上传失败,课程ID: {}", uploadDto.getCourseId(), e);
|
||||
throw new RuntimeException("原子批量上传失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理单个内容项
|
||||
*/
|
||||
private BatchUploadResponseDto.UploadResult processContentItem(
|
||||
BatchUploadWithNullDto.ContentItem item, int itemIndex, Integer operationType,
|
||||
String courseId, String csectionId,
|
||||
String orgId, String orgName, String resOwner1, String resOwner2, String resOwner3,
|
||||
String ownership1, String ownership2, String ownership3) {
|
||||
|
||||
BatchUploadResponseDto.UploadResult result = new BatchUploadResponseDto.UploadResult();
|
||||
result.setItemIndex(itemIndex);
|
||||
result.setContentName(item.getContentName());
|
||||
result.setContentType(item.getContentType());
|
||||
result.setResourceType(item.getResourceType());
|
||||
|
||||
try {
|
||||
String contentId;
|
||||
|
||||
if (operationType == 1) { // 新增操作
|
||||
contentId = createContentItem(item, courseId, csectionId,
|
||||
orgId, orgName, resOwner1, resOwner2, resOwner3,
|
||||
ownership1, ownership2, ownership3);
|
||||
} else if (operationType == 2) { // 更新操作
|
||||
contentId = updateContentItem(item, courseId, csectionId,
|
||||
orgId, orgName, resOwner1, resOwner2, resOwner3,
|
||||
ownership1, ownership2, ownership3);
|
||||
} else {
|
||||
throw new RuntimeException("不支持的操作类型: " + operationType);
|
||||
}
|
||||
|
||||
result.setContentId(contentId);
|
||||
result.setSuccess(true);
|
||||
result.setMessage("处理成功");
|
||||
|
||||
} catch (Exception e) {
|
||||
result.setSuccess(false);
|
||||
result.setMessage("处理失败: " + e.getMessage());
|
||||
log.error("处理第{}项内容失败: {}", itemIndex + 1, e.getMessage(), e);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建内容项
|
||||
*/
|
||||
private String createContentItem(
|
||||
BatchUploadWithNullDto.ContentItem item, String courseId, String csectionId,
|
||||
String orgId, String orgName, String resOwner1, String resOwner2, String resOwner3,
|
||||
String ownership1, String ownership2, String ownership3) {
|
||||
|
||||
// 验证新增操作的必要字段
|
||||
if (item.getContentType() == null) {
|
||||
throw new RuntimeException("新增操作时内容类型不能为null");
|
||||
}
|
||||
|
||||
// 创建课程内容
|
||||
CourseContent courseContent = new CourseContent();
|
||||
courseContent.setCourseId(courseId);
|
||||
courseContent.setContentName(getContentName(item, true));
|
||||
courseContent.setContentType(item.getContentType());
|
||||
courseContent.setSortIndex(item.getSortIndex() != null ? item.getSortIndex() : 0);
|
||||
courseContent.setCsectionId(csectionId);
|
||||
courseContent.setDuration(item.getDuration() != null ? item.getDuration() : 0);
|
||||
courseContent.setDeleted(item.getDeleted() != null ? item.getDeleted() : false);
|
||||
|
||||
courseContentDao.save(courseContent);
|
||||
String contentId = courseContent.getId();
|
||||
log.info("创建课程内容成功,ID: {}, 类型: {}", contentId, item.getContentType());
|
||||
|
||||
// 根据内容类型处理具体内容
|
||||
processContentByType(item, contentId, courseId, 1,
|
||||
orgId, orgName, resOwner1, resOwner2, resOwner3,
|
||||
ownership1, ownership2, ownership3);
|
||||
|
||||
return contentId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新内容项(更新操作)
|
||||
*/
|
||||
private String updateContentItem(
|
||||
BatchUploadWithNullDto.ContentItem item, String courseId, String csectionId,
|
||||
String orgId, String orgName, String resOwner1, String resOwner2, String resOwner3,
|
||||
String ownership1, String ownership2, String ownership3) {
|
||||
|
||||
// 验证内容ID
|
||||
if (StringUtils.isBlank(item.getContentId())) {
|
||||
throw new RuntimeException("更新操作时内容ID不能为空");
|
||||
}
|
||||
|
||||
// 获取现有内容
|
||||
CourseContent courseContent = courseContentDao.get(item.getContentId());
|
||||
if (courseContent == null) {
|
||||
throw new RuntimeException("课程内容不存在: " + item.getContentId());
|
||||
}
|
||||
|
||||
// 验证课程ID匹配
|
||||
if (!courseContent.getCourseId().equals(courseId)) {
|
||||
throw new RuntimeException("课程ID不匹配");
|
||||
}
|
||||
|
||||
String contentId = item.getContentId();
|
||||
|
||||
// 更新课程内容(只更新非null字段)
|
||||
updateCourseContentWithNull(courseContent, item, csectionId);
|
||||
courseContentDao.update(courseContent);
|
||||
log.info("更新课程内容成功,ID: {}", contentId);
|
||||
|
||||
// 根据内容类型处理具体内容
|
||||
processContentByType(item, contentId, courseId, 2,
|
||||
orgId, orgName, resOwner1, resOwner2, resOwner3,
|
||||
ownership1, ownership2, ownership3);
|
||||
|
||||
return contentId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新课程内容(只更新非null字段)
|
||||
*/
|
||||
private void updateCourseContentWithNull(CourseContent courseContent,
|
||||
BatchUploadWithNullDto.ContentItem item,
|
||||
String csectionId) {
|
||||
if (item.getContentName() != null) {
|
||||
courseContent.setContentName(item.getContentName());
|
||||
}
|
||||
if (item.getContentType() != null) {
|
||||
courseContent.setContentType(item.getContentType());
|
||||
}
|
||||
if (item.getSortIndex() != null) {
|
||||
courseContent.setSortIndex(item.getSortIndex());
|
||||
}
|
||||
if (csectionId != null) {
|
||||
courseContent.setCsectionId(csectionId);
|
||||
}
|
||||
if (item.getDuration() != null) {
|
||||
courseContent.setDuration(item.getDuration());
|
||||
}
|
||||
if (item.getDeleted() != null) {
|
||||
courseContent.setDeleted(item.getDeleted());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据内容类型处理具体内容
|
||||
*/
|
||||
private void processContentByType(
|
||||
BatchUploadWithNullDto.ContentItem item, String contentId, String courseId,
|
||||
Integer operationType,
|
||||
String orgId, String orgName, String resOwner1, String resOwner2, String resOwner3,
|
||||
String ownership1, String ownership2, String ownership3) {
|
||||
|
||||
Integer contentType = item.getContentType();
|
||||
|
||||
switch (contentType != null ? contentType : 0) {
|
||||
case 1: // 课件
|
||||
processCourseware(item, contentId, courseId, operationType,
|
||||
orgId, orgName, resOwner1, resOwner2, resOwner3,
|
||||
ownership1, ownership2, ownership3);
|
||||
break;
|
||||
case 2: // 作业
|
||||
processHomework(item, contentId, courseId, operationType);
|
||||
break;
|
||||
case 3: // 考试
|
||||
processExam(item, contentId, courseId, operationType);
|
||||
break;
|
||||
case 4: // 评估
|
||||
processAssess(item, contentId, courseId, operationType);
|
||||
break;
|
||||
case 0: // contentType为null(更新时不修改类型)
|
||||
// 不处理,保持原内容类型
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理课件
|
||||
*/
|
||||
private void processCourseware(
|
||||
BatchUploadWithNullDto.ContentItem item, String contentId, String courseId,
|
||||
Integer operationType,
|
||||
String orgId, String orgName, String resOwner1, String resOwner2, String resOwner3,
|
||||
String ownership1, String ownership2, String ownership3) {
|
||||
|
||||
// 如果是新增操作,resourceType不能为null
|
||||
if (operationType == 1 && item.getResourceType() == null) {
|
||||
throw new RuntimeException("新增课件时资源类型不能为null");
|
||||
}
|
||||
|
||||
// 如果resourceType不为null,处理对应的资源
|
||||
if (item.getResourceType() != null) {
|
||||
List<CourseResourceUploadDto.CourseFileResource> resources =
|
||||
convertToCourseFileResources(item);
|
||||
|
||||
if (!resources.isEmpty()) {
|
||||
// 调用资源处理服务
|
||||
courseResourceService.processCourseResources(contentId, resources,
|
||||
orgId, orgName,
|
||||
resOwner1, resOwner2, resOwner3,
|
||||
ownership1, ownership2, ownership3);
|
||||
log.info("处理课件资源成功,内容ID: {}, 资源类型: {}", contentId, item.getResourceType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为课程文件资源
|
||||
*/
|
||||
private List<CourseResourceUploadDto.CourseFileResource> convertToCourseFileResources(
|
||||
BatchUploadWithNullDto.ContentItem item) {
|
||||
|
||||
List<CourseResourceUploadDto.CourseFileResource> resources = new ArrayList<>();
|
||||
|
||||
// 根据资源类型创建对应的资源
|
||||
if (item.getResourceType() != null) {
|
||||
CourseResourceUploadDto.CourseFileResource resource =
|
||||
new CourseResourceUploadDto.CourseFileResource();
|
||||
|
||||
resource.setResType(item.getResourceType());
|
||||
resource.setName(item.getContentName() != null ? item.getContentName() :
|
||||
getResourceTypeName(item.getResourceType()) + "资源");
|
||||
resource.setDevice(3); // 默认多端可见
|
||||
|
||||
// 处理文件资源(视频、音频、文档、SCORM包)
|
||||
if (item.getFileResource() != null &&
|
||||
(item.getResourceType() == 10 || item.getResourceType() == 20 ||
|
||||
item.getResourceType() == 40 || item.getResourceType() == 50)) {
|
||||
|
||||
BatchUploadWithNullDto.FileResourceInfo fileInfo = item.getFileResource();
|
||||
resource.setFileBase64(fileInfo.getFileBase64());
|
||||
resource.setOriginalFileName(fileInfo.getOriginalFileName());
|
||||
resource.setDown(fileInfo.getDown());
|
||||
resource.setRemark(fileInfo.getRemark());
|
||||
resource.setDevice(fileInfo.getDevice() != null ? fileInfo.getDevice() : 3);
|
||||
resource.setDecoder(fileInfo.getDecoder());
|
||||
resource.setVideoWidth(fileInfo.getVideoWidth());
|
||||
resource.setVideoHeight(fileInfo.getVideoHeight());
|
||||
}
|
||||
|
||||
// 处理图文资源
|
||||
if (item.getGraphicTextResource() != null && item.getResourceType() == 41) {
|
||||
BatchUploadWithNullDto.GraphicTextResourceInfo textInfo = item.getGraphicTextResource();
|
||||
CourseResourceUploadDto.GraphicTextContent graphicText =
|
||||
new CourseResourceUploadDto.GraphicTextContent();
|
||||
|
||||
graphicText.setTitle(textInfo.getTitle());
|
||||
graphicText.setContent(textInfo.getContent());
|
||||
graphicText.setImageBase64(textInfo.getImageBase64());
|
||||
graphicText.setImageName(textInfo.getImageName());
|
||||
|
||||
if (textInfo.getFormat() != null) {
|
||||
CourseResourceUploadDto.TextFormat format = new CourseResourceUploadDto.TextFormat();
|
||||
format.setBold(textInfo.getFormat().getBold());
|
||||
format.setItalic(textInfo.getFormat().getItalic());
|
||||
format.setUnderline(textInfo.getFormat().getUnderline());
|
||||
format.setStrikethrough(textInfo.getFormat().getStrikethrough());
|
||||
graphicText.setFormat(format);
|
||||
}
|
||||
|
||||
graphicText.setFontSize(textInfo.getFontSize());
|
||||
graphicText.setLineHeight(textInfo.getLineHeight());
|
||||
graphicText.setTextAlign(textInfo.getTextAlign());
|
||||
|
||||
resource.setGraphicText(graphicText);
|
||||
}
|
||||
|
||||
// 处理外部链接资源
|
||||
if (item.getExternalLinkResource() != null && item.getResourceType() == 90) {
|
||||
BatchUploadWithNullDto.ExternalLinkResourceInfo linkInfo = item.getExternalLinkResource();
|
||||
CourseResourceUploadDto.ExternalLinkContent externalLink =
|
||||
new CourseResourceUploadDto.ExternalLinkContent();
|
||||
|
||||
externalLink.setUrl(linkInfo.getUrl());
|
||||
externalLink.setOpenType(linkInfo.getOpenType() != null ? linkInfo.getOpenType() : 2);
|
||||
externalLink.setTitle(linkInfo.getTitle());
|
||||
externalLink.setDescription(linkInfo.getDescription());
|
||||
|
||||
resource.setExternalLink(externalLink);
|
||||
}
|
||||
|
||||
resources.add(resource);
|
||||
}
|
||||
|
||||
return resources;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理作业
|
||||
*/
|
||||
private void processHomework(BatchUploadWithNullDto.ContentItem item,
|
||||
String contentId, String courseId,
|
||||
Integer operationType) {
|
||||
|
||||
if (operationType == 1) { // 新增操作
|
||||
if (item.getHomeworkInfo() == null) {
|
||||
throw new RuntimeException("新增作业时作业信息不能为null");
|
||||
}
|
||||
|
||||
BatchUploadWithNullDto.HomeworkInfo homeworkInfo = item.getHomeworkInfo();
|
||||
CourseHomeWork homework = new CourseHomeWork();
|
||||
homework.setContentId(contentId);
|
||||
homework.setCourseId(courseId);
|
||||
homework.setName(homeworkInfo.getName());
|
||||
homework.setContent(homeworkInfo.getContent());
|
||||
homework.setFile(homeworkInfo.getFile());
|
||||
homework.setDeadTime(homeworkInfo.getDeadTime());
|
||||
homework.setSubmitMode(homeworkInfo.getSubmitMode() != null ?
|
||||
homeworkInfo.getSubmitMode() : 1);
|
||||
|
||||
courseHomeWorkDao.save(homework);
|
||||
log.info("创建作业成功,内容ID: {}", contentId);
|
||||
|
||||
} else if (operationType == 2 && item.getHomeworkInfo() != null) { // 更新操作
|
||||
// 更新操作:查找现有作业
|
||||
CourseHomeWork existingHomework = courseHomeWorkDao.findOne(
|
||||
FieldFilters.eq("contentId", contentId));
|
||||
|
||||
if (existingHomework != null) {
|
||||
// 更新现有作业(只更新非null字段)
|
||||
updateHomeworkWithNull(existingHomework, item.getHomeworkInfo());
|
||||
courseHomeWorkDao.update(existingHomework);
|
||||
log.info("更新作业成功,内容ID: {}", contentId);
|
||||
} else {
|
||||
// 创建新作业
|
||||
BatchUploadWithNullDto.HomeworkInfo homeworkInfo = item.getHomeworkInfo();
|
||||
CourseHomeWork homework = new CourseHomeWork();
|
||||
homework.setContentId(contentId);
|
||||
homework.setCourseId(courseId);
|
||||
homework.setName(homeworkInfo.getName());
|
||||
homework.setContent(homeworkInfo.getContent());
|
||||
homework.setFile(homeworkInfo.getFile());
|
||||
homework.setDeadTime(homeworkInfo.getDeadTime());
|
||||
homework.setSubmitMode(homeworkInfo.getSubmitMode() != null ?
|
||||
homeworkInfo.getSubmitMode() : 1);
|
||||
|
||||
courseHomeWorkDao.save(homework);
|
||||
log.info("创建作业成功,内容ID: {}", contentId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新作业(只更新非null字段)
|
||||
*/
|
||||
private void updateHomeworkWithNull(CourseHomeWork homework,
|
||||
BatchUploadWithNullDto.HomeworkInfo homeworkInfo) {
|
||||
if (homeworkInfo.getName() != null) {
|
||||
homework.setName(homeworkInfo.getName());
|
||||
}
|
||||
if (homeworkInfo.getContent() != null) {
|
||||
homework.setContent(homeworkInfo.getContent());
|
||||
}
|
||||
if (homeworkInfo.getFile() != null) {
|
||||
homework.setFile(homeworkInfo.getFile());
|
||||
}
|
||||
if (homeworkInfo.getDeadTime() != null) {
|
||||
homework.setDeadTime(homeworkInfo.getDeadTime());
|
||||
}
|
||||
if (homeworkInfo.getSubmitMode() != null) {
|
||||
homework.setSubmitMode(homeworkInfo.getSubmitMode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理考试
|
||||
*/
|
||||
private void processExam(BatchUploadWithNullDto.ContentItem item,
|
||||
String contentId, String courseId,
|
||||
Integer operationType) {
|
||||
|
||||
if (operationType == 1) { // 新增操作
|
||||
if (item.getExamInfo() == null) {
|
||||
throw new RuntimeException("新增考试时考试信息不能为null");
|
||||
}
|
||||
|
||||
CourseExam exam = createExamFromInfo(contentId, courseId, item.getExamInfo());
|
||||
courseExamDao.save(exam);
|
||||
log.info("创建考试成功,内容ID: {}", contentId);
|
||||
|
||||
} else if (operationType == 2 && item.getExamInfo() != null) { // 更新操作
|
||||
// 更新操作:查找现有考试
|
||||
CourseExam existingExam = courseExamDao.findOne(
|
||||
FieldFilters.eq("contentId", contentId));
|
||||
|
||||
if (existingExam != null) {
|
||||
// 更新现有考试(只更新非null字段)
|
||||
updateExamWithNull(existingExam, item.getExamInfo());
|
||||
courseExamDao.update(existingExam);
|
||||
log.info("更新考试成功,内容ID: {}", contentId);
|
||||
} else {
|
||||
// 创建新考试
|
||||
CourseExam exam = createExamFromInfo(contentId, courseId, item.getExamInfo());
|
||||
courseExamDao.save(exam);
|
||||
log.info("创建考试成功,内容ID: {}", contentId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建考试信息
|
||||
*/
|
||||
private CourseExam createExamFromInfo(String contentId, String courseId,
|
||||
BatchUploadWithNullDto.ExamInfo examInfo) {
|
||||
CourseExam exam = new CourseExam();
|
||||
exam.setContentId(contentId);
|
||||
exam.setCourseId(courseId);
|
||||
exam.setTestName(examInfo.getTestName());
|
||||
exam.setTestDuration(examInfo.getTestDuration());
|
||||
exam.setShowAnalysis(examInfo.getShowAnalysis() != null ? examInfo.getShowAnalysis() : false);
|
||||
exam.setShowAnswer(examInfo.getShowAnswer() != null ? examInfo.getShowAnswer() : false);
|
||||
exam.setTimes(examInfo.getTimes() != null ? examInfo.getTimes() : 0);
|
||||
exam.setArrange(examInfo.getArrange() != null ? examInfo.getArrange() : 0);
|
||||
exam.setScoringType(examInfo.getScoringType() != null ? examInfo.getScoringType() : 1);
|
||||
exam.setPassLine(examInfo.getPassLine() != null ? examInfo.getPassLine() : 60);
|
||||
exam.setRandomMode(examInfo.getRandomMode() != null ? examInfo.getRandomMode() : false);
|
||||
exam.setQnum(examInfo.getQnum());
|
||||
exam.setQscore(examInfo.getQscore());
|
||||
exam.setPaperType(examInfo.getPaperType());
|
||||
exam.setPercentScore(examInfo.getPercentScore() != null ? examInfo.getPercentScore() : true);
|
||||
exam.setPaperId(examInfo.getPaperId());
|
||||
exam.setPaperContent(examInfo.getPaperContent());
|
||||
exam.setInfo(examInfo.getInfo());
|
||||
|
||||
return exam;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新考试信息
|
||||
*/
|
||||
private void updateExamWithNull(CourseExam exam, BatchUploadWithNullDto.ExamInfo examInfo) {
|
||||
if (examInfo.getTestName() != null) {
|
||||
exam.setTestName(examInfo.getTestName());
|
||||
}
|
||||
if (examInfo.getTestDuration() != null) {
|
||||
exam.setTestDuration(examInfo.getTestDuration());
|
||||
}
|
||||
if (examInfo.getShowAnalysis() != null) {
|
||||
exam.setShowAnalysis(examInfo.getShowAnalysis());
|
||||
}
|
||||
if (examInfo.getShowAnswer() != null) {
|
||||
exam.setShowAnswer(examInfo.getShowAnswer());
|
||||
}
|
||||
if (examInfo.getTimes() != null) {
|
||||
exam.setTimes(examInfo.getTimes());
|
||||
}
|
||||
if (examInfo.getArrange() != null) {
|
||||
exam.setArrange(examInfo.getArrange());
|
||||
}
|
||||
if (examInfo.getScoringType() != null) {
|
||||
exam.setScoringType(examInfo.getScoringType());
|
||||
}
|
||||
if (examInfo.getPassLine() != null) {
|
||||
exam.setPassLine(examInfo.getPassLine());
|
||||
}
|
||||
if (examInfo.getRandomMode() != null) {
|
||||
exam.setRandomMode(examInfo.getRandomMode());
|
||||
}
|
||||
if (examInfo.getQnum() != null) {
|
||||
exam.setQnum(examInfo.getQnum());
|
||||
}
|
||||
if (examInfo.getQscore() != null) {
|
||||
exam.setQscore(examInfo.getQscore());
|
||||
}
|
||||
if (examInfo.getPaperType() != null) {
|
||||
exam.setPaperType(examInfo.getPaperType());
|
||||
}
|
||||
if (examInfo.getPercentScore() != null) {
|
||||
exam.setPercentScore(examInfo.getPercentScore());
|
||||
}
|
||||
if (examInfo.getPaperId() != null) {
|
||||
exam.setPaperId(examInfo.getPaperId());
|
||||
}
|
||||
if (examInfo.getPaperContent() != null) {
|
||||
exam.setPaperContent(examInfo.getPaperContent());
|
||||
}
|
||||
if (examInfo.getInfo() != null) {
|
||||
exam.setInfo(examInfo.getInfo());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理评估
|
||||
*/
|
||||
private void processAssess(BatchUploadWithNullDto.ContentItem item,
|
||||
String contentId, String courseId,
|
||||
Integer operationType) {
|
||||
|
||||
if (operationType == 1) { // 新增操作
|
||||
if (item.getAssessInfo() == null) {
|
||||
throw new RuntimeException("新增评估时评估信息不能为null");
|
||||
}
|
||||
|
||||
CourseAssess assess = createAssessFromInfo(contentId, courseId, item.getAssessInfo());
|
||||
courseAssessDao.save(assess);
|
||||
log.info("创建评估成功,内容ID: {}", contentId);
|
||||
|
||||
} else if (operationType == 2 && item.getAssessInfo() != null) { // 更新操作
|
||||
// 更新操作:查找现有评估
|
||||
CourseAssess existingAssess = courseAssessDao.findOne(
|
||||
FieldFilters.eq("contentId", contentId));
|
||||
|
||||
if (existingAssess != null) {
|
||||
// 更新现有评估(只更新非null字段)
|
||||
updateAssessWithNull(existingAssess, item.getAssessInfo());
|
||||
courseAssessDao.update(existingAssess);
|
||||
log.info("更新评估成功,内容ID: {}", contentId);
|
||||
} else {
|
||||
// 创建新评估
|
||||
CourseAssess assess = createAssessFromInfo(contentId, courseId, item.getAssessInfo());
|
||||
courseAssessDao.save(assess);
|
||||
log.info("创建评估成功,内容ID: {}", contentId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建评估信息
|
||||
*/
|
||||
private CourseAssess createAssessFromInfo(String contentId, String courseId,
|
||||
BatchUploadWithNullDto.AssessInfo assessInfo) {
|
||||
CourseAssess assess = new CourseAssess();
|
||||
assess.setContentId(contentId);
|
||||
assess.setCourseId(courseId);
|
||||
assess.setAssessId(assessInfo.getAssessId());
|
||||
assess.setQuestion(assessInfo.getQuestion());
|
||||
assess.setQType(assessInfo.getQType() != null ? assessInfo.getQType() : 1);
|
||||
|
||||
return assess;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新评估信息
|
||||
*/
|
||||
private void updateAssessWithNull(CourseAssess assess, BatchUploadWithNullDto.AssessInfo assessInfo) {
|
||||
if (assessInfo.getAssessId() != null) {
|
||||
assess.setAssessId(assessInfo.getAssessId());
|
||||
}
|
||||
if (assessInfo.getQuestion() != null) {
|
||||
assess.setQuestion(assessInfo.getQuestion());
|
||||
}
|
||||
if (assessInfo.getQType() != null) {
|
||||
assess.setQType(assessInfo.getQType());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取内容名称
|
||||
*/
|
||||
private String getContentName(BatchUploadWithNullDto.ContentItem item, boolean isNew) {
|
||||
if (item.getContentName() != null) {
|
||||
return item.getContentName();
|
||||
}
|
||||
|
||||
if (isNew) {
|
||||
// 新增操作:生成默认名称
|
||||
if (item.getContentType() != null) {
|
||||
switch (item.getContentType()) {
|
||||
case 1:
|
||||
String resourceTypeName = "课件";
|
||||
if (item.getResourceType() != null) {
|
||||
resourceTypeName = getResourceTypeName(item.getResourceType());
|
||||
}
|
||||
return resourceTypeName + "-" + UUID.randomUUID().toString().substring(0, 8);
|
||||
case 2:
|
||||
return item.getHomeworkInfo() != null && item.getHomeworkInfo().getName() != null ?
|
||||
item.getHomeworkInfo().getName() : "未命名作业";
|
||||
case 3:
|
||||
return item.getExamInfo() != null && item.getExamInfo().getTestName() != null ?
|
||||
item.getExamInfo().getTestName() : "未命名考试";
|
||||
case 4:
|
||||
return "课程评估";
|
||||
default:
|
||||
return "未命名内容";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取资源类型名称
|
||||
*/
|
||||
private String getResourceTypeName(Integer resType) {
|
||||
if (resType == null) return "资源";
|
||||
switch (resType) {
|
||||
case 10: return "视频";
|
||||
case 20: return "音频";
|
||||
case 40: return "文档";
|
||||
case 41: return "图文";
|
||||
case 50: return "SCORM包";
|
||||
case 90: return "外部链接";
|
||||
default: return "资源";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,17 +11,21 @@ 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.IFieldUpdate;
|
||||
import com.xboe.core.orm.UpdateBuilder;
|
||||
import com.xboe.module.course.dao.*;
|
||||
import com.xboe.module.course.dto.CourseContentDto;
|
||||
import com.xboe.module.course.dto.CourseWareSaveDto;
|
||||
import com.xboe.module.course.dto.SortItem;
|
||||
import com.xboe.module.course.entity.CourseAssess;
|
||||
import com.xboe.module.course.entity.CourseContent;
|
||||
import com.xboe.module.course.entity.CourseExam;
|
||||
import com.xboe.module.course.entity.CourseHomeWork;
|
||||
import com.xboe.module.course.service.ICourseContentService;
|
||||
import com.xboe.module.course.service.TypeTreeService;
|
||||
import com.xboe.module.exam.dao.ExamPaperDao;
|
||||
import com.xboe.module.exam.vo.TestQuestionVo;
|
||||
import com.xboe.module.teacher.dao.TeacherDao;
|
||||
import com.xboe.standard.enums.BoedxContentType;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@@ -29,11 +33,24 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.transaction.Transactional;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.xboe.module.course.entity.Course;
|
||||
import com.xboe.module.course.entity.CourseCrowd;
|
||||
import com.xboe.module.course.entity.CourseTeacher;
|
||||
import com.xboe.module.teacher.entity.Teacher;
|
||||
import com.xboe.module.course.dao.CourseDao;
|
||||
import com.xboe.module.course.dao.CourseCrowdDao;
|
||||
import com.xboe.module.course.dao.CourseTeacherDao;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import com.xboe.module.course.dao.*;
|
||||
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class CourseContentServiceImpl implements ICourseContentService {
|
||||
@@ -59,58 +76,452 @@ public class CourseContentServiceImpl implements ICourseContentService {
|
||||
@Resource
|
||||
private ExamPaperDao examPaperDao;
|
||||
|
||||
@Resource
|
||||
private CourseDao courseDao;
|
||||
|
||||
@Resource
|
||||
private CourseTeacherDao courseTeacherDao;
|
||||
|
||||
@Resource
|
||||
private CourseCrowdDao courseCrowdDao;
|
||||
|
||||
@Resource
|
||||
private TeacherDao teacherDao;
|
||||
|
||||
@Resource
|
||||
private TypeTreeService typeTreeService;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void saveOrUpdate(CourseContentDto dto) {
|
||||
CourseContent cc=dto.getContent();
|
||||
CourseAssess assess=dto.getAssess();
|
||||
CourseExam exam=dto.getExam();
|
||||
CourseHomeWork homework=dto.getHomework();
|
||||
|
||||
CourseContent cc=dto.getContent(); // 获取内容信息
|
||||
CourseAssess assess=dto.getAssess(); // 获取评估信息
|
||||
CourseExam exam=dto.getExam(); // 获取考试信息
|
||||
CourseHomeWork homework=dto.getHomework(); // 获取作业信息
|
||||
// 当新增的情况cc.getId() 为空,
|
||||
// 表示这是一个新创建的对象,而不是更新已有对象
|
||||
if(StringUtils.isBlank(cc.getId())) {
|
||||
|
||||
//新增的情况
|
||||
|
||||
// 设置课程内容的删除状态标记,
|
||||
// 采用的是"软删除".
|
||||
// 重点:表示没有删除
|
||||
cc.setDeleted(false);
|
||||
// 检查课程内容的时长是否为空
|
||||
if(cc.getDuration()==null) {
|
||||
// 如果时长为空,将其设置为默认值 0
|
||||
cc.setDuration(0);
|
||||
}
|
||||
//如果是没有目录的,并具是课程内容
|
||||
|
||||
|
||||
//检查是否为无目录的课程
|
||||
if(dto.getType()!=null && dto.getType()==10) {
|
||||
// 如果是无目录的课程,检查是否是表课件,如果是表的课件
|
||||
if(cc.getSortIndex()==1) {
|
||||
//先删除之前其它的
|
||||
// 防止同一课程下出现多个具有相同特殊标识的内容项,比如我之前封面
|
||||
ccDao.deleteByField("courseId",cc.getCourseId());
|
||||
}
|
||||
}
|
||||
ccDao.save(cc);
|
||||
ccDao.save(cc); // 将信息保存下来
|
||||
}else {
|
||||
ccDao.update(cc);
|
||||
cc.setSysVersion(ccDao.getVersion(cc.getId()));
|
||||
cc.setSysVersion(ccDao.getVersion(cc.getId())); // 保存我的版本
|
||||
}
|
||||
//添加或保存其它信息
|
||||
//保存其它信息
|
||||
if(assess!=null) {
|
||||
assess.setCourseId(cc.getCourseId());
|
||||
assessDao.saveOrUpdate(assess);
|
||||
assess.setCourseId(cc.getCourseId()); // 将我的课程id信息保存进去
|
||||
assessDao.saveOrUpdate(assess); // 保存评估信息
|
||||
}
|
||||
if(exam!=null) {
|
||||
// 校验选择是否合法
|
||||
if ((exam.getRandomMode() && !(exam.getQnum() > 0)) || (!exam.getRandomMode() && exam.getQnum() > 0)) {
|
||||
throw new RuntimeException("随机选题处参数错误");
|
||||
}
|
||||
exam.setCourseId(cc.getCourseId());
|
||||
exam.setContentId(cc.getId());
|
||||
exam.setCourseId(cc.getCourseId()); // 设置课程id
|
||||
exam.setContentId(cc.getId()); // 设置内容id
|
||||
// 是否是百分之测试
|
||||
if(exam.getPercentScore()==null) {
|
||||
exam.setPercentScore(true);
|
||||
exam.setPercentScore(true); // 如果不是的话,改成我的迷人百分制测试
|
||||
}
|
||||
examDao.saveOrUpdate(exam);
|
||||
exam.setSysVersion(examDao.getVersion(exam.getId()));
|
||||
examDao.saveOrUpdate(exam); // 将内容进行保存
|
||||
exam.setSysVersion(examDao.getVersion(exam.getId())); // 保存我的版本的ID编号
|
||||
}
|
||||
if(homework!=null) {
|
||||
homework.setCourseId(cc.getCourseId());
|
||||
homework.setContentId(cc.getId());
|
||||
homework=homeworkDao.saveOrUpdate(homework);
|
||||
homework.setSysVersion(homeworkDao.getVersion(homework.getId()));
|
||||
homework.setCourseId(cc.getCourseId()); // 设置课程id
|
||||
homework.setContentId(cc.getId()); // 设置内容id
|
||||
homework=homeworkDao.saveOrUpdate(homework); // 保存作业信息
|
||||
homework.setSysVersion(homeworkDao.getVersion(homework.getId())); // 保存我的版本的ID编号
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void saveOrUpdateCourseware(CourseWareSaveDto dto) {
|
||||
try {
|
||||
// 1. 基础校验
|
||||
validateBaseInfo(dto);
|
||||
|
||||
// 2. 校验分类和归属的层级关系
|
||||
validateTypeHierarchy(dto);
|
||||
|
||||
// 3. 核心逻辑:只根据课程ID是否为null判断是新增还是更新
|
||||
String courseIdFromDto = dto.getCourse().getId();
|
||||
boolean isNew = (courseIdFromDto == null);
|
||||
String courseId;
|
||||
|
||||
if (isNew) {
|
||||
// 如果课程ID为null,执行新增操作
|
||||
log.info("课程ID为null,执行新增操作");
|
||||
courseId = createNewCourse(dto.getCourse());
|
||||
} else {
|
||||
// 如果课程ID不为null,执行更新操作
|
||||
log.info("课程ID不为null,执行更新操作,courseId={}", courseIdFromDto);
|
||||
Course existingCourse = courseDao.get(courseIdFromDto);
|
||||
if (existingCourse != null) {
|
||||
courseId = updateExistingCourse(existingCourse, dto.getCourse());
|
||||
} else {
|
||||
// 课程ID不为null但找不到对应课程,仍然创建新课程
|
||||
log.info("课程ID '{}' 在数据库中不存在,执行新增操作", courseIdFromDto);
|
||||
courseId = createNewCourse(dto.getCourse());
|
||||
}
|
||||
}
|
||||
// 4. 绑定分类(三级)
|
||||
bindClassification(courseId, dto);
|
||||
|
||||
// 5. 绑定资源归属(三级)
|
||||
bindResourceOwnership(courseId, dto);
|
||||
|
||||
// 6. 处理观看设置
|
||||
if (dto.getDevice() != null) {
|
||||
courseDao.updateFieldById(courseId, "device", dto.getDevice());
|
||||
} else {
|
||||
// 默认多端可看
|
||||
courseDao.updateFieldById(courseId, "device", 3);
|
||||
}
|
||||
|
||||
// 7. 处理授课教师
|
||||
processTeachers(courseId, dto.getTeacherList());
|
||||
|
||||
// 8. 处理受众列表
|
||||
processCrowds(courseId, dto.getCrowdList());
|
||||
|
||||
log.info("课程课件保存成功,操作类型:{},courseId={}",
|
||||
isNew ? "新增" : "更新", courseId);
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
log.error("课程课件保存/更新异常:{}", e.getMessage(), e);
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
log.error("课程课件保存/更新系统异常", e);
|
||||
throw new RuntimeException("课程课件保存/更新失败:" + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 基础信息校验
|
||||
*/
|
||||
private void validateBaseInfo(CourseWareSaveDto dto) {
|
||||
if (dto == null) {
|
||||
throw new RuntimeException("课程课件参数不能为空");
|
||||
}
|
||||
if (StringUtils.isBlank(dto.getCourse().getName())) {
|
||||
throw new RuntimeException("课程名称不能为空");
|
||||
}
|
||||
if (CollectionUtils.isEmpty(dto.getTeacherList())) {
|
||||
throw new RuntimeException("至少选择一位授课教师");
|
||||
}
|
||||
if (dto.getSysType1() == null || StringUtils.isBlank(dto.getSysType1().getId())) {
|
||||
throw new RuntimeException("课程一级分类不能为空");
|
||||
}
|
||||
if (dto.getResOwner1() == null || StringUtils.isBlank(dto.getResOwner1().getId())) {
|
||||
throw new RuntimeException("资源一级归属不能为空");
|
||||
}
|
||||
if (dto.getDevice() == null || (dto.getDevice() < 1 || dto.getDevice() > 3)) {
|
||||
throw new RuntimeException("观看设置参数错误,必须是1、2或3");
|
||||
}
|
||||
|
||||
// 课程名称长度校验
|
||||
if (dto.getCourse().getName().length() > 100) {
|
||||
throw new RuntimeException("课程名称不能超过100字");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验分类和归属的层级关系
|
||||
*/
|
||||
private void validateTypeHierarchy(CourseWareSaveDto dto) {
|
||||
// 校验分类层级
|
||||
if (dto.getSysType2() != null && StringUtils.isNotBlank(dto.getSysType2().getId())) {
|
||||
if (dto.getSysType1() == null || StringUtils.isBlank(dto.getSysType1().getId())) {
|
||||
throw new RuntimeException("选择了二级分类必须有一级分类");
|
||||
}
|
||||
// 验证二级分类的父级是否是一级分类
|
||||
if (!typeTreeService.validateTypeHierarchy(dto.getSysType2().getId(), dto.getSysType1().getId())) {
|
||||
throw new RuntimeException("二级分类的父级必须是一级分类");
|
||||
}
|
||||
}
|
||||
|
||||
if (dto.getSysType3() != null && StringUtils.isNotBlank(dto.getSysType3().getId())) {
|
||||
if (dto.getSysType2() == null || StringUtils.isBlank(dto.getSysType2().getId())) {
|
||||
throw new RuntimeException("选择了三级分类必须有二级分类");
|
||||
}
|
||||
if (!typeTreeService.validateTypeHierarchy(dto.getSysType3().getId(), dto.getSysType2().getId())) {
|
||||
throw new RuntimeException("三级分类的父级必须是二级分类");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建新课程
|
||||
*/
|
||||
private String createNewCourse(CourseWareSaveDto.CourseInfo courseInfo) {
|
||||
Course course = new Course();
|
||||
// 设置基础信息
|
||||
course.setName(courseInfo.getName());
|
||||
course.setValue(courseInfo.getValue());
|
||||
course.setSummary(courseInfo.getSummary());
|
||||
course.setForUsers(courseInfo.getForUsers());
|
||||
course.setTags(courseInfo.getTags());
|
||||
course.setCoverImg(courseInfo.getCoverImg());
|
||||
course.setType(courseInfo.getType());
|
||||
course.setOrgId(courseInfo.getOrgId());
|
||||
course.setEnabled(courseInfo.getEnabled());
|
||||
course.setIsTop(courseInfo.getIsTop());
|
||||
course.setStudyTime(courseInfo.getStudyTime());
|
||||
course.setOverview(courseInfo.getOverview());
|
||||
course.setOpenCourse(courseInfo.getOpenCourse());
|
||||
course.setVisible(courseInfo.getVisible());
|
||||
course.setOrderStudy(courseInfo.getOrderStudy());
|
||||
course.setKeywords(courseInfo.getKeywords());
|
||||
course.setForScene(courseInfo.getForScene());
|
||||
course.setCreateFrom(courseInfo.getCreateFrom());
|
||||
|
||||
// 设置默认值
|
||||
setDefaultCourseValues(course);
|
||||
|
||||
courseDao.save(course);
|
||||
String courseId = course.getId();
|
||||
log.info("新增课程成功,courseId={}, 课程名称={}", courseId, course.getName());
|
||||
return courseId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新现有课程
|
||||
*/
|
||||
private String updateExistingCourse(Course existingCourse, CourseWareSaveDto.CourseInfo newCourseData) {
|
||||
String courseId = existingCourse.getId();
|
||||
|
||||
// 使用UpdateBuilder更新字段,避免直接修改实体
|
||||
List<IFieldUpdate> updates = new ArrayList<>();
|
||||
|
||||
// 更新基础信息
|
||||
updates.add(UpdateBuilder.create("name", newCourseData.getName()));
|
||||
if (newCourseData.getValue() != null) {
|
||||
updates.add(UpdateBuilder.create("value", newCourseData.getValue()));
|
||||
}
|
||||
if (newCourseData.getSummary() != null) {
|
||||
updates.add(UpdateBuilder.create("summary", newCourseData.getSummary()));
|
||||
}
|
||||
if (newCourseData.getForUsers() != null) {
|
||||
updates.add(UpdateBuilder.create("forUsers", newCourseData.getForUsers()));
|
||||
}
|
||||
if (newCourseData.getTags() != null) {
|
||||
updates.add(UpdateBuilder.create("tags", newCourseData.getTags()));
|
||||
}
|
||||
if (newCourseData.getCoverImg() != null) {
|
||||
updates.add(UpdateBuilder.create("coverImg", newCourseData.getCoverImg()));
|
||||
}
|
||||
if (newCourseData.getType() != null) {
|
||||
updates.add(UpdateBuilder.create("type", newCourseData.getType()));
|
||||
}
|
||||
if (newCourseData.getStudyTime() != null) {
|
||||
updates.add(UpdateBuilder.create("studyTime", newCourseData.getStudyTime()));
|
||||
}
|
||||
if (newCourseData.getOverview() != null) {
|
||||
updates.add(UpdateBuilder.create("overview", newCourseData.getOverview()));
|
||||
}
|
||||
|
||||
// 版本号递增
|
||||
// updates.add(UpdateBuilder.create("sysVersion", existingCourse.getSysVersion() + 1));
|
||||
Integer currentVersion = existingCourse.getSysVersion();
|
||||
if (currentVersion == null) {
|
||||
currentVersion = 0; // 如果为null,当做0处理
|
||||
}
|
||||
updates.add(UpdateBuilder.create("sysVersion", currentVersion + 1));
|
||||
// 执行更新
|
||||
for (IFieldUpdate update : updates) {
|
||||
courseDao.updateFieldById(courseId, update.getField(), update.getValue());
|
||||
}
|
||||
|
||||
|
||||
log.info("更新课程成功,courseId={}, 课程名称={}", courseId, newCourseData.getName());
|
||||
return courseId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置课程默认值
|
||||
*/
|
||||
private void setDefaultCourseValues(Course course) {
|
||||
course.setDeleted(false);
|
||||
course.setStatus(Course.STATUS_NONE); // 草稿状态
|
||||
course.setPublished(false);
|
||||
course.setEnabled(true);
|
||||
course.setVisible(true);
|
||||
course.setErasable(true);
|
||||
course.setIsTop(false);
|
||||
course.setOrderStudy(false);
|
||||
|
||||
// 设置统计默认值
|
||||
course.setViews(0);
|
||||
course.setComments(0);
|
||||
course.setPraises(0);
|
||||
course.setShares(0);
|
||||
course.setFavorites(0);
|
||||
course.setStudys(0);
|
||||
course.setScore(0f);
|
||||
course.setTrampleCount(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定分类(三级)
|
||||
*/
|
||||
private void bindClassification(String courseId, CourseWareSaveDto dto) {
|
||||
List<IFieldUpdate> updates = new ArrayList<>();
|
||||
|
||||
if (dto.getSysType1() != null) {
|
||||
updates.add(UpdateBuilder.create("sysType1", dto.getSysType1().getId()));
|
||||
}
|
||||
|
||||
if (dto.getSysType2() != null && StringUtils.isNotBlank(dto.getSysType2().getId())) {
|
||||
updates.add(UpdateBuilder.create("sysType2", dto.getSysType2().getId()));
|
||||
} else {
|
||||
updates.add(UpdateBuilder.create("sysType2", null));
|
||||
}
|
||||
|
||||
if (dto.getSysType3() != null && StringUtils.isNotBlank(dto.getSysType3().getId())) {
|
||||
updates.add(UpdateBuilder.create("sysType3", dto.getSysType3().getId()));
|
||||
} else {
|
||||
updates.add(UpdateBuilder.create("sysType3", null));
|
||||
}
|
||||
|
||||
if (!updates.isEmpty()) {
|
||||
for (IFieldUpdate update : updates) {
|
||||
courseDao.updateFieldById(courseId, update.getField(), update.getValue());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定资源归属(三级)
|
||||
*/
|
||||
private void bindResourceOwnership(String courseId, CourseWareSaveDto dto) {
|
||||
List<IFieldUpdate> updates = new ArrayList<>();
|
||||
|
||||
if (dto.getResOwner1() != null) {
|
||||
updates.add(UpdateBuilder.create("resOwner1", dto.getResOwner1().getId()));
|
||||
}
|
||||
|
||||
if (dto.getResOwner2() != null && StringUtils.isNotBlank(dto.getResOwner2().getId())) {
|
||||
updates.add(UpdateBuilder.create("resOwner2", dto.getResOwner2().getId()));
|
||||
} else {
|
||||
updates.add(UpdateBuilder.create("resOwner2", null));
|
||||
}
|
||||
|
||||
if (dto.getResOwner3() != null && StringUtils.isNotBlank(dto.getResOwner3().getId())) {
|
||||
updates.add(UpdateBuilder.create("resOwner3", dto.getResOwner3().getId()));
|
||||
} else {
|
||||
updates.add(UpdateBuilder.create("resOwner3", null));
|
||||
}
|
||||
|
||||
if (!updates.isEmpty()) {
|
||||
for (IFieldUpdate update : updates) {
|
||||
courseDao.updateFieldById(courseId, update.getField(), update.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理授课教师
|
||||
*/
|
||||
private void processTeachers(String courseId, List<CourseTeacher> teacherList) {
|
||||
// 1. 先删除该课程下旧的教师关联
|
||||
courseTeacherDao.deleteByField("courseId", courseId);
|
||||
|
||||
// 2. 遍历新增新的教师关联
|
||||
for (CourseTeacher teacher : teacherList) {
|
||||
// 校验教师ID非空
|
||||
if (StringUtils.isBlank(teacher.getTeacherId())) {
|
||||
throw new RuntimeException("添加失败:教师ID不能为空");
|
||||
}
|
||||
|
||||
// 核心:校验教师姓名必须传(null/空串/全空格都判定为未传)
|
||||
// if (StringUtils.isBlank(teacher.getTeacherName())) {
|
||||
// throw new RuntimeException("添加失败:教师姓名必须填写,teacherId=" + teacher.getTeacherId());
|
||||
// }
|
||||
|
||||
// 3. 查询教师是否存在,不存在则自动新增
|
||||
Teacher existTeacher = teacherDao.get(teacher.getTeacherId());
|
||||
if (existTeacher == null) {
|
||||
log.warn("教师ID{}不存在",
|
||||
teacher.getTeacherId(), teacher.getTeacherName());
|
||||
|
||||
// 构建新教师实体
|
||||
existTeacher = new Teacher();
|
||||
existTeacher.setId(teacher.getTeacherId());
|
||||
existTeacher.setName(teacher.getTeacherName());
|
||||
|
||||
// 基础默认值
|
||||
existTeacher.setDeleted(false);
|
||||
existTeacher.setSysVersion(1);
|
||||
|
||||
// 保存新教师
|
||||
teacherDao.save(existTeacher);
|
||||
log.info("自动新增教师成功,teacherId={}, 教师姓名={}",
|
||||
existTeacher.getId(), existTeacher.getName());
|
||||
}
|
||||
|
||||
// 4. 绑定课程ID并保存关联关系(姓名已校验非空)
|
||||
// teacher.setCourseId(courseId);
|
||||
// courseTeacherDao.save(teacher);
|
||||
|
||||
teacher.setCourseId(courseId);
|
||||
|
||||
// 清除ID和版本号,避免主键冲突
|
||||
|
||||
teacher.setId(null);
|
||||
courseTeacherDao.save(teacher);
|
||||
}
|
||||
|
||||
log.info("处理授课教师完成,courseId={}, 教师数量={}", courseId, teacherList.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理受众列表
|
||||
*/
|
||||
private void processCrowds(String courseId, List<CourseCrowd> crowdList) {
|
||||
if (CollectionUtils.isNotEmpty(crowdList)) {
|
||||
// 先删除旧关联
|
||||
courseCrowdDao.deleteByField("courseId", courseId);
|
||||
|
||||
// 新增新关联
|
||||
for (CourseCrowd crowd : crowdList) {
|
||||
if (StringUtils.isBlank(crowd.getGroupId())) {
|
||||
throw new RuntimeException("受众ID不能为空");
|
||||
}
|
||||
crowd.setCourseId(courseId); // 绑定课程ID
|
||||
courseCrowdDao.save(crowd);
|
||||
}
|
||||
|
||||
log.info("处理受众完成,courseId={}, 受众数量={}", courseId, crowdList.size());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void delete(String id,int contentType,boolean flag) {
|
||||
|
||||
@@ -0,0 +1,537 @@
|
||||
package com.xboe.module.course.service.impl;
|
||||
|
||||
import com.xboe.module.course.dto.BatchUploadResponseDto;
|
||||
import com.xboe.module.course.dto.BatchUploadWithNullDto;
|
||||
import com.xboe.module.course.dto.CourseResourceUploadDto;
|
||||
import com.xboe.module.course.service.CourseContentValidationService;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 课程内容验证 服务实现类
|
||||
*/
|
||||
@Service
|
||||
public class CourseContentValidationServiceImpl implements CourseContentValidationService {
|
||||
|
||||
/**
|
||||
* 验证批量上传DTO
|
||||
*/
|
||||
@Override
|
||||
public List<String> validateBatchUpload(BatchUploadWithNullDto dto) {
|
||||
List<String> errors = new ArrayList<>();
|
||||
|
||||
if (StringUtils.isBlank(dto.getCourseId())) {
|
||||
errors.add("课程ID不能为空");
|
||||
}
|
||||
|
||||
if (dto.getOperationType() == null) {
|
||||
errors.add("操作类型不能为空");
|
||||
} else if (dto.getOperationType() != 1 ) {
|
||||
errors.add("操作类型必须是1(新增)");
|
||||
}
|
||||
|
||||
if (CollectionUtils.isEmpty(dto.getContentItems())) {
|
||||
errors.add("内容项列表不能为空");
|
||||
return errors;
|
||||
}
|
||||
|
||||
// 验证每个内容项
|
||||
for (int i = 0; i < dto.getContentItems().size(); i++) {
|
||||
BatchUploadWithNullDto.ContentItem item = dto.getContentItems().get(i);
|
||||
List<String> itemErrors = validateContentItem(item, dto.getOperationType(), i + 1);
|
||||
errors.addAll(itemErrors);
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证内容项
|
||||
*/
|
||||
@Override
|
||||
public List<String> validateContentItem(BatchUploadWithNullDto.ContentItem item, Integer operationType, int index) {
|
||||
List<String> errors = new ArrayList<>();
|
||||
|
||||
// 验证操作类型
|
||||
if (operationType == 2) { // 更新操作
|
||||
if (StringUtils.isBlank(item.getContentId())) {
|
||||
errors.add("第" + index + "项:更新操作时内容ID不能为空");
|
||||
}
|
||||
} else if (operationType == 1) { // 新增操作
|
||||
// contentType不能为null
|
||||
if (item.getContentType() == null) {
|
||||
errors.add("第" + index + "项:新增操作时内容类型不能为空");
|
||||
} else {
|
||||
validateForCreate(errors, item, index);
|
||||
}
|
||||
}
|
||||
|
||||
// 验证
|
||||
validateNonNullFields(errors, item, index);
|
||||
|
||||
// 验证章节ID
|
||||
if (StringUtils.isBlank(item.getCsectionId())) {
|
||||
errors.add("第" + index + "项:章节ID不能为空");
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证资源上传DTO
|
||||
*/
|
||||
@Override
|
||||
public String validateResource(CourseResourceUploadDto.CourseFileResource resource) {
|
||||
Integer resType = resource.getResType();
|
||||
|
||||
switch (resType) {
|
||||
case 10: // 视频
|
||||
case 20: // 音频
|
||||
case 40: // 文档
|
||||
case 50: // SCORM包
|
||||
if (StringUtils.isBlank(resource.getFileBase64())) {
|
||||
return getResourceTypeName(resType) + "文件内容不能为空";
|
||||
}
|
||||
if (StringUtils.isBlank(resource.getOriginalFileName())) {
|
||||
return getResourceTypeName(resType) + "文件名称不能为空";
|
||||
}
|
||||
|
||||
if (!isValidFileExtension(resType, resource.getOriginalFileName())) {
|
||||
return getResourceTypeName(resType) + "文件格式不支持";
|
||||
}
|
||||
break;
|
||||
case 41: // 图文
|
||||
if (resource.getGraphicText() == null) {
|
||||
return "图文内容不能为空";
|
||||
}
|
||||
if (StringUtils.isBlank(resource.getGraphicText().getTitle())) {
|
||||
return "图文标题不能为空";
|
||||
}
|
||||
if (StringUtils.isBlank(resource.getGraphicText().getContent())) {
|
||||
return "图文内容不能为空";
|
||||
}
|
||||
break;
|
||||
case 90: // 外部链接
|
||||
if (resource.getExternalLink() == null) {
|
||||
return "外部链接内容不能为空";
|
||||
}
|
||||
if (StringUtils.isBlank(resource.getExternalLink().getUrl())) {
|
||||
return "链接地址不能为空";
|
||||
}
|
||||
if (resource.getExternalLink().getOpenType() != null &&
|
||||
resource.getExternalLink().getOpenType() != 1 &&
|
||||
resource.getExternalLink().getOpenType() != 2) {
|
||||
return "链接打开方式必须是1(页面嵌入)或2(新窗口打开)";
|
||||
}
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证资源类型是否有效
|
||||
*/
|
||||
@Override
|
||||
public boolean isValidResourceType(Integer resType) {
|
||||
return resType != null && (
|
||||
resType == 10 || // 视频
|
||||
resType == 20 || // 音频
|
||||
resType == 40 || // 文档
|
||||
resType == 41 || // 图文
|
||||
resType == 50 || // SCORM包
|
||||
resType == 90 // 外部链接
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证文件扩展名
|
||||
*/
|
||||
@Override
|
||||
public boolean isValidFileExtension(Integer resType, String fileName) {
|
||||
String extension = getFileExtension(fileName);
|
||||
if (StringUtils.isBlank(extension)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (resType) {
|
||||
case 10: // 视频
|
||||
return "mp4".equalsIgnoreCase(extension);
|
||||
case 20: // 音频
|
||||
return "mp3".equalsIgnoreCase(extension);
|
||||
case 40: // 文档
|
||||
return extension.matches("(?i)(doc|docx|xls|xlsx|pptx|txt|pdf)");
|
||||
case 50: // SCORM包
|
||||
return "zip".equalsIgnoreCase(extension);
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件扩展名
|
||||
*/
|
||||
@Override
|
||||
public String getFileExtension(String filename) {
|
||||
if (StringUtils.isBlank(filename) || !filename.contains(".")) {
|
||||
return "";
|
||||
}
|
||||
return filename.substring(filename.lastIndexOf(".") + 1).toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取资源类型名称
|
||||
*/
|
||||
@Override
|
||||
public String getResourceTypeName(Integer resType) {
|
||||
switch (resType) {
|
||||
case 10: return "视频";
|
||||
case 20: return "音频";
|
||||
case 40: return "文档";
|
||||
case 41: return "图文";
|
||||
case 50: return "SCORM包";
|
||||
case 90: return "外部链接";
|
||||
default: return "未知资源";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个新的批量上传响应DTO
|
||||
* @param courseId 课程ID
|
||||
* @param totalCount 总数
|
||||
* @return BatchUploadResponseDto
|
||||
*/
|
||||
@Override
|
||||
public BatchUploadResponseDto createBatchUploadResponse(String courseId, int totalCount) {
|
||||
BatchUploadResponseDto response = new BatchUploadResponseDto();
|
||||
response.setCourseId(courseId);
|
||||
response.setTotalCount(totalCount);
|
||||
response.setSuccessCount(0);
|
||||
response.setFailCount(0);
|
||||
response.setResults(new ArrayList<>());
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加结果到批量上传响应DTO
|
||||
* @param response 响应DTO
|
||||
* @param result 结果
|
||||
*/
|
||||
@Override
|
||||
public void addResultToBatchUploadResponse(BatchUploadResponseDto response, BatchUploadResponseDto.UploadResult result) {
|
||||
if (result.isSuccess()) {
|
||||
response.setSuccessCount(response.getSuccessCount() + 1);
|
||||
} else {
|
||||
response.setFailCount(response.getFailCount() + 1);
|
||||
}
|
||||
|
||||
response.getResults().add(result);
|
||||
response.setSuccess(response.getFailCount() == 0);
|
||||
response.setMessage(String.format("上传完成,成功:%d,失败:%d", response.getSuccessCount(), response.getFailCount()));
|
||||
}
|
||||
|
||||
// ------------------------ 私有辅助方法(保留在实现类中) ------------------------
|
||||
private void validateForCreate(List<String> errors, BatchUploadWithNullDto.ContentItem item, int index) {
|
||||
switch (item.getContentType()) {
|
||||
case 1: // 课件
|
||||
validateCoursewareForCreate(errors, item, index);
|
||||
break;
|
||||
case 2: // 作业
|
||||
validateHomeworkForCreate(errors, item, index);
|
||||
break;
|
||||
case 3: // 考试
|
||||
validateExamForCreate(errors, item, index);
|
||||
break;
|
||||
case 4: // 评估
|
||||
validateAssessForCreate(errors, item, index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void validateCoursewareForCreate(List<String> errors, BatchUploadWithNullDto.ContentItem item, int index) {
|
||||
if (item.getResourceType() == null) {
|
||||
errors.add("第" + index + "项:新增课件时资源类型不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (item.getResourceType()) {
|
||||
case 10: // 视频
|
||||
case 20: // 音频
|
||||
case 40: // 文档
|
||||
case 50: // SCORM包
|
||||
validateFileResourceForCreate(errors, item, index, item.getResourceType());
|
||||
break;
|
||||
case 41: // 图文
|
||||
validateGraphicTextForCreate(errors, item, index);
|
||||
break;
|
||||
case 90: // 外部链接
|
||||
validateExternalLinkForCreate(errors, item, index);
|
||||
break;
|
||||
default:
|
||||
errors.add("第" + index + "项:不支持的资源类型: " + item.getResourceType());
|
||||
}
|
||||
}
|
||||
|
||||
private void validateFileResourceForCreate(List<String> errors, BatchUploadWithNullDto.ContentItem item, int index, Integer resourceType) {
|
||||
if (item.getFileResource() == null) {
|
||||
errors.add("第" + index + "项:新增" + getResourceTypeName(resourceType) + "时文件资源不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
// 文件内容必填
|
||||
if (StringUtils.isBlank(item.getFileResource().getFileBase64())) {
|
||||
errors.add("第" + index + "项:" + getResourceTypeName(resourceType) + "文件内容不能为空");
|
||||
}
|
||||
|
||||
// 文件名必填
|
||||
if (StringUtils.isBlank(item.getFileResource().getOriginalFileName())) {
|
||||
errors.add("第" + index + "项:" + getResourceTypeName(resourceType) + "文件名称不能为空");
|
||||
} else if (!isValidFileExtension(resourceType, item.getFileResource().getOriginalFileName())) {
|
||||
errors.add("第" + index + "项:" + getResourceTypeName(resourceType) + "文件格式不支持");
|
||||
}
|
||||
}
|
||||
|
||||
private void validateGraphicTextForCreate(List<String> errors, BatchUploadWithNullDto.ContentItem item, int index) {
|
||||
if (item.getGraphicTextResource() == null) {
|
||||
errors.add("第" + index + "项:新增图文时图文资源不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
// 标题必填
|
||||
if (StringUtils.isBlank(item.getGraphicTextResource().getTitle())) {
|
||||
errors.add("第" + index + "项:图文标题不能为空");
|
||||
}
|
||||
|
||||
// 内容必填
|
||||
if (StringUtils.isBlank(item.getGraphicTextResource().getContent())) {
|
||||
errors.add("第" + index + "项:图文内容不能为空");
|
||||
}
|
||||
}
|
||||
|
||||
private void validateExternalLinkForCreate(List<String> errors, BatchUploadWithNullDto.ContentItem item, int index) {
|
||||
if (item.getExternalLinkResource() == null) {
|
||||
errors.add("第" + index + "项:新增外部链接时链接资源不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
// 链接地址必填
|
||||
if (StringUtils.isBlank(item.getExternalLinkResource().getUrl())) {
|
||||
errors.add("第" + index + "项:链接地址不能为空");
|
||||
}
|
||||
}
|
||||
|
||||
private void validateHomeworkForCreate(List<String> errors, BatchUploadWithNullDto.ContentItem item, int index) {
|
||||
if (item.getHomeworkInfo() == null) {
|
||||
errors.add("第" + index + "项:新增作业时作业信息不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(item.getHomeworkInfo().getName())) {
|
||||
errors.add("第" + index + "项:作业名称不能为空");
|
||||
}
|
||||
|
||||
if (item.getHomeworkInfo().getSubmitMode() == null) {
|
||||
errors.add("第" + index + "项:提交模式不能为空");
|
||||
}
|
||||
}
|
||||
|
||||
private void validateExamForCreate(List<String> errors, BatchUploadWithNullDto.ContentItem item, int index) {
|
||||
if (item.getExamInfo() == null) {
|
||||
errors.add("第" + index + "项:新增考试时考试信息不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(item.getExamInfo().getTestName())) {
|
||||
errors.add("第" + index + "项:考试名称不能为空");
|
||||
}
|
||||
|
||||
if (item.getExamInfo().getPaperType() == null) {
|
||||
errors.add("第" + index + "项:试卷类型不能为空");
|
||||
}
|
||||
|
||||
// 根据试卷类型验证
|
||||
if (item.getExamInfo().getPaperType() != null) {
|
||||
if (item.getExamInfo().getPaperType() == 1 && StringUtils.isBlank(item.getExamInfo().getPaperContent())) {
|
||||
errors.add("第" + index + "项:自定义试卷内容不能为空");
|
||||
}
|
||||
if (item.getExamInfo().getPaperType() == 2 && StringUtils.isBlank(item.getExamInfo().getPaperId())) {
|
||||
errors.add("第" + index + "项:试卷ID不能为空");
|
||||
}
|
||||
}
|
||||
|
||||
// 验证随机选题参数
|
||||
if (item.getExamInfo().getRandomMode() != null && item.getExamInfo().getRandomMode()) {
|
||||
if (item.getExamInfo().getQnum() == null || item.getExamInfo().getQnum() <= 0) {
|
||||
errors.add("第" + index + "项:随机选题模式下,试题数量必须大于0");
|
||||
}
|
||||
if (item.getExamInfo().getQscore() == null || item.getExamInfo().getQscore() <= 0) {
|
||||
errors.add("第" + index + "项:随机选题模式下,试题分值必须大于0");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void validateAssessForCreate(List<String> errors, BatchUploadWithNullDto.ContentItem item, int index) {
|
||||
if (item.getAssessInfo() == null) {
|
||||
errors.add("第" + index + "项:新增评估时评估信息不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(item.getAssessInfo().getQuestion())) {
|
||||
errors.add("第" + index + "项:评估问题不能为空");
|
||||
}
|
||||
|
||||
if (item.getAssessInfo().getQType() == null) {
|
||||
errors.add("第" + index + "项:问题类型不能为空");
|
||||
}
|
||||
}
|
||||
|
||||
private void validateNonNullFields(List<String> errors, BatchUploadWithNullDto.ContentItem item, int index) {
|
||||
if (item.getContentType() != null) {
|
||||
switch (item.getContentType()) {
|
||||
case 1: // 课件
|
||||
validateCoursewareNonNull(errors, item, index);
|
||||
break;
|
||||
case 2: // 作业
|
||||
validateHomeworkNonNull(errors, item, index);
|
||||
break;
|
||||
case 3: // 考试
|
||||
validateExamNonNull(errors, item, index);
|
||||
break;
|
||||
case 4: // 评估
|
||||
validateAssessNonNull(errors, item, index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void validateCoursewareNonNull(List<String> errors, BatchUploadWithNullDto.ContentItem item, int index) {
|
||||
if (item.getResourceType() != null) {
|
||||
if (!isValidResourceType(item.getResourceType())) {
|
||||
errors.add("第" + index + "项:不支持的资源类型: " + item.getResourceType());
|
||||
return;
|
||||
}
|
||||
|
||||
switch (item.getResourceType()) {
|
||||
case 10: // 视频
|
||||
case 20: // 音频
|
||||
case 40: // 文档
|
||||
case 50: // SCORM包
|
||||
if (item.getFileResource() != null) {
|
||||
validateFileResourceNonNull(item.getFileResource(), item.getResourceType(), index, errors);
|
||||
}
|
||||
break;
|
||||
case 41: // 图文
|
||||
if (item.getGraphicTextResource() != null) {
|
||||
validateGraphicTextResourceNonNull(item.getGraphicTextResource(), index, errors);
|
||||
}
|
||||
break;
|
||||
case 90: // 外部链接
|
||||
if (item.getExternalLinkResource() != null) {
|
||||
validateExternalLinkResourceNonNull(item.getExternalLinkResource(), index, errors);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void validateFileResourceNonNull(BatchUploadWithNullDto.FileResourceInfo resource, Integer resourceType,
|
||||
int index, List<String> errors) {
|
||||
if (resource.getFileBase64() != null) {
|
||||
if (StringUtils.isBlank(resource.getOriginalFileName())) {
|
||||
errors.add("第" + index + "项:" + getResourceTypeName(resourceType) + "文件名称不能为空");
|
||||
} else if (!isValidFileExtension(resourceType, resource.getOriginalFileName())) {
|
||||
errors.add("第" + index + "项:" + getResourceTypeName(resourceType) + "文件格式不支持");
|
||||
}
|
||||
}
|
||||
|
||||
if (resource.getDevice() != null && (resource.getDevice() < 1 || resource.getDevice() > 3)) {
|
||||
errors.add("第" + index + "项:设备类型必须是1,2或3");
|
||||
}
|
||||
}
|
||||
|
||||
private void validateGraphicTextResourceNonNull(BatchUploadWithNullDto.GraphicTextResourceInfo resource,
|
||||
int index, List<String> errors) {
|
||||
|
||||
if (resource.getTitle() != null && StringUtils.isBlank(resource.getTitle())) {
|
||||
errors.add("第" + index + "项:图文标题不能为空");
|
||||
}
|
||||
|
||||
if (resource.getContent() != null && StringUtils.isBlank(resource.getContent())) {
|
||||
errors.add("第" + index + "项:图文内容不能为空");
|
||||
}
|
||||
|
||||
if (resource.getImageBase64() != null && StringUtils.isBlank(resource.getImageName())) {
|
||||
errors.add("第" + index + "项:图片名称不能为空");
|
||||
}
|
||||
}
|
||||
|
||||
private void validateExternalLinkResourceNonNull(BatchUploadWithNullDto.ExternalLinkResourceInfo resource,
|
||||
int index, List<String> errors) {
|
||||
|
||||
if (resource.getUrl() != null && StringUtils.isBlank(resource.getUrl())) {
|
||||
errors.add("第" + index + "项:链接地址不能为空");
|
||||
}
|
||||
|
||||
if (resource.getOpenType() != null &&
|
||||
(resource.getOpenType() != 1 && resource.getOpenType() != 2)) {
|
||||
errors.add("第" + index + "项:链接打开方式必须是1(页面嵌入)或2(新窗口打开)");
|
||||
}
|
||||
}
|
||||
|
||||
private void validateHomeworkNonNull(List<String> errors, BatchUploadWithNullDto.ContentItem item, int index) {
|
||||
if (item.getHomeworkInfo() != null) {
|
||||
|
||||
if (item.getHomeworkInfo().getName() != null && StringUtils.isBlank(item.getHomeworkInfo().getName())) {
|
||||
errors.add("第" + index + "项:作业名称不能为空");
|
||||
}
|
||||
|
||||
if (item.getHomeworkInfo().getSubmitMode() != null &&
|
||||
(item.getHomeworkInfo().getSubmitMode() < 1 || item.getHomeworkInfo().getSubmitMode() > 3)) {
|
||||
errors.add("第" + index + "项:提交模式必须是1,2或3");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void validateExamNonNull(List<String> errors, BatchUploadWithNullDto.ContentItem item, int index) {
|
||||
if (item.getExamInfo() != null) {
|
||||
|
||||
if (item.getExamInfo().getTestName() != null && StringUtils.isBlank(item.getExamInfo().getTestName())) {
|
||||
errors.add("第" + index + "项:考试名称不能为空");
|
||||
}
|
||||
|
||||
if (item.getExamInfo().getPaperType() != null) {
|
||||
if (item.getExamInfo().getPaperType() == 1 &&
|
||||
item.getExamInfo().getPaperContent() != null &&
|
||||
StringUtils.isBlank(item.getExamInfo().getPaperContent())) {
|
||||
errors.add("第" + index + "项:自定义试卷内容不能为空");
|
||||
}
|
||||
if (item.getExamInfo().getPaperType() == 2 &&
|
||||
item.getExamInfo().getPaperId() != null &&
|
||||
StringUtils.isBlank(item.getExamInfo().getPaperId())) {
|
||||
errors.add("第" + index + "项:试卷ID不能为空");
|
||||
}
|
||||
}
|
||||
|
||||
if (item.getExamInfo().getRandomMode() != null && item.getExamInfo().getRandomMode()) {
|
||||
if (item.getExamInfo().getQnum() != null && item.getExamInfo().getQnum() <= 0) {
|
||||
errors.add("第" + index + "项:随机选题模式下,试题数量必须大于0");
|
||||
}
|
||||
if (item.getExamInfo().getQscore() != null && item.getExamInfo().getQscore() <= 0) {
|
||||
errors.add("第" + index + "项:随机选题模式下,试题分值必须大于0");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void validateAssessNonNull(List<String> errors, BatchUploadWithNullDto.ContentItem item, int index) {
|
||||
if (item.getAssessInfo() != null) {
|
||||
|
||||
if (item.getAssessInfo().getQuestion() != null && StringUtils.isBlank(item.getAssessInfo().getQuestion())) {
|
||||
errors.add("第" + index + "项:评估问题不能为空");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@ import java.util.List;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import com.xboe.core.orm.UpdateBuilder;
|
||||
import com.xboe.module.course.dto.BPMCallbackDto;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -19,6 +21,7 @@ import com.xboe.module.course.entity.CourseHRBPAudit;
|
||||
import com.xboe.module.course.service.ICourseHRBPAuditService;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@@ -26,6 +29,31 @@ public class CourseHRBPAuditServiceImpl implements ICourseHRBPAuditService {
|
||||
|
||||
@Resource
|
||||
CourseHRBPAuditDao courseHRBPAuditDao;
|
||||
/**
|
||||
* BPM回调后更新审批结果
|
||||
* @param dto
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public void bpmRecallUpdate(BPMCallbackDto dto){
|
||||
if(dto.getAuditResult().equals("success")){
|
||||
courseHRBPAuditDao.update(
|
||||
UpdateBuilder.from(CourseHRBPAudit.class)
|
||||
.addUpdateField("auditResult", 1)
|
||||
.addFilter(FieldFilters.eq("auditId", dto.getAuditId()))
|
||||
.builder()
|
||||
);
|
||||
}else{
|
||||
courseHRBPAuditDao.update(
|
||||
UpdateBuilder.from(CourseHRBPAudit.class)
|
||||
.addUpdateField("auditResult", 0)
|
||||
.addUpdateField("auditRemark", dto.getAuditComment())
|
||||
.addUpdateField("auditUser", dto.getLastAuditUser())
|
||||
.addFilter(FieldFilters.eq("auditId", dto.getAuditId()))
|
||||
.builder()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(CourseHRBPAudit coursePersonAudit) {
|
||||
@@ -171,6 +199,19 @@ public class CourseHRBPAuditServiceImpl implements ICourseHRBPAuditService {
|
||||
return hrbp;
|
||||
}
|
||||
|
||||
/**
|
||||
* 25.12.10新增
|
||||
* 检查课程是否已经有过审核记录了
|
||||
* @param courseId
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public CourseHRBPAudit hadAuditing(String courseId) {
|
||||
//未审核的
|
||||
CourseHRBPAudit hrbp = courseHRBPAuditDao.findOne(FieldFilters.eq("courseId", courseId),FieldFilters.eq("status", 9));
|
||||
return hrbp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageList<CourseHRBPAudit> pageList(Integer pageIndex, Integer pageSize, int userType, CourseHRBPAudit info) {
|
||||
QueryBuilder query=QueryBuilder.from(CourseHRBPAudit.class.getSimpleName()+" a,"+Course.class.getSimpleName()+" c");
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
package com.xboe.module.course.service.impl;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.xboe.core.utils.OkHttpUtil;
|
||||
import com.xboe.module.course.dto.BPMResponseDto;
|
||||
import com.xboe.module.course.dto.CourseFullDto;
|
||||
import com.xboe.module.course.service.ICourseManageService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class CourseManageServiceImpl implements ICourseManageService {
|
||||
@Autowired
|
||||
private OkHttpUtil okHttpUtil;
|
||||
/**
|
||||
* 启用/停用调用外部接口前讲DTO转换成JSON字符串
|
||||
*/
|
||||
@Override
|
||||
public String prepareDisableAuditRequest(CourseFullDto dto) {
|
||||
try {
|
||||
// 创建顶层对象
|
||||
Map<String, Object> request = new HashMap<>();
|
||||
|
||||
// 构造auditContent部分
|
||||
Map<String, Object> auditContent = new HashMap<>();
|
||||
//auditContent.put("auditType", "停用课程");
|
||||
if(dto.getAuditType()==0)
|
||||
{
|
||||
auditContent.put("auditType", "停用课程");
|
||||
}
|
||||
else{
|
||||
auditContent.put("auditType", "启用课程");
|
||||
}
|
||||
|
||||
// 构造courseInfo部分
|
||||
Map<String, String> courseInfo = new HashMap<>();
|
||||
courseInfo.put("name", dto.getCourse().getName());
|
||||
auditContent.put("courseInfo", courseInfo);
|
||||
|
||||
// 将auditContent放入请求体
|
||||
request.put("auditContent", auditContent);
|
||||
|
||||
// 添加用户ID
|
||||
request.put("userId", dto.getUserId());
|
||||
|
||||
// 转换为JSON字符串
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
return objectMapper.writeValueAsString(request);
|
||||
|
||||
} catch (Exception e) {
|
||||
|
||||
throw new RuntimeException("构造停用审核JSON请求体失败", e);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 创建调用外部接口前讲DTO转换成JSON字符串
|
||||
*/
|
||||
@Override
|
||||
public String prepareCreateAuditRequest(CourseFullDto dto) {
|
||||
try {
|
||||
// 创建顶层对象
|
||||
Map<String, Object> request = new HashMap<>();
|
||||
|
||||
// 构造auditContent部分
|
||||
Map<String, Object> auditContent = new HashMap<>();
|
||||
auditContent.put("auditType", "创建课程");
|
||||
// 构造courseInfo部分
|
||||
Map<String, Object> courseInfo = new HashMap<>();
|
||||
courseInfo.put("name", dto.getCourse().getName());
|
||||
//几级分类?几级归属?
|
||||
courseInfo.put("sysType", dto.getCourse().getSysType1());
|
||||
courseInfo.put("resOwner", dto.getCourse().getResOwner1());
|
||||
courseInfo.put("teacherName", dto.getCourse().getTeacher());
|
||||
courseInfo.put("forUsers", dto.getCourse().getForUsers());
|
||||
courseInfo.put("tags", dto.getCourse().getTags());
|
||||
courseInfo.put("device", dto.getCourse().getDevice());
|
||||
courseInfo.put("coverImg", dto.getCourse().getCoverImg());
|
||||
courseInfo.put("value", dto.getCourse().getValue());
|
||||
courseInfo.put("summary", dto.getCourse().getSummary());
|
||||
//课程目录,等到拿到BPM接口,需要查看getCatalogTree()得到的格式是否与BPM需要的格式一致
|
||||
courseInfo.put("sections", dto.getCatalogTree());
|
||||
auditContent.put("courseInfo", courseInfo);
|
||||
|
||||
// 将auditContent放入请求体
|
||||
request.put("auditContent", auditContent);
|
||||
|
||||
// 添加用户ID
|
||||
request.put("userId", dto.getUserId());
|
||||
|
||||
// 转换为JSON字符串
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
return objectMapper.writeValueAsString(request);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("构造创建审核JSON请求体失败", e);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 调用BPM外部接口
|
||||
*/
|
||||
@Override
|
||||
public BPMResponseDto callBPMInterface(String jsonRequestBody){
|
||||
try {
|
||||
String token = "";
|
||||
// BPM接口地址,先按照接口文档写,得到实际的地址之后再修改
|
||||
String url = "http://bpm-system-address/xboe/bpm/audit/submit";
|
||||
// 设置请求头
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
headers.put("Content-Type", "application/json");
|
||||
// 添加认证头
|
||||
headers.put("Authorization", "Bearer " + token);
|
||||
// Map转String数组
|
||||
List<String> headerList = new ArrayList<>();
|
||||
for (Map.Entry<String, String> entry : headers.entrySet()) {
|
||||
headerList.add(entry.getKey() + ":" + entry.getValue());
|
||||
}
|
||||
String[] headerArr = headerList.toArray(new String[0]);
|
||||
// 发送POST请求
|
||||
String response = okHttpUtil.doPostJson(url, jsonRequestBody,headerArr);
|
||||
// 解析响应
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
return objectMapper.readValue(response, BPMResponseDto.class);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("调用BPM接口失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -82,65 +82,9 @@ public class CoursePageServiceImpl implements ICoursePageService {
|
||||
|
||||
String currentAccountId = currentUser == null ? null : currentUser.getAccountId();
|
||||
|
||||
// 第一版废弃
|
||||
// // 构建查询条件
|
||||
// List<IFieldFilter> filters = new ArrayList<>();
|
||||
//
|
||||
// // 自动添加过滤已删除
|
||||
// filters.add(FieldFilters.eq("c.deleted", false));
|
||||
//
|
||||
// // 添加权限过滤条件
|
||||
// if (!isSystemAdmin) {
|
||||
// // 非系统管理员需要进行权限过滤
|
||||
// List<IFieldFilter> permissionFilters = new ArrayList<>();
|
||||
//
|
||||
// // 添加组织权限过滤
|
||||
// if (orgIds != null && !orgIds.isEmpty()) {
|
||||
// permissionFilters.add(FieldFilters.in("c.orgId", orgIds));
|
||||
// }
|
||||
//
|
||||
// // 添加创建人过滤(可以看到自己创建的课程)
|
||||
// if (StringUtils.isNotBlank(currentAccountId)) {
|
||||
// permissionFilters.add(FieldFilters.eq("c.sysCreateAid", currentAccountId));
|
||||
// }
|
||||
//
|
||||
// // 如果有权限条件,则添加OR条件
|
||||
// if (!permissionFilters.isEmpty()) {
|
||||
// filters.add(FieldFilters.or(permissionFilters));
|
||||
// }
|
||||
// }
|
||||
// filters.addAll(createFilters(coursePageQueryDTO));
|
||||
//
|
||||
// QueryBuilder query = QueryBuilder.from(Course.class.getSimpleName() + " c").addFilters(filters);
|
||||
// // 处理排序
|
||||
// handleOrder(query, coursePageQueryDTO.getOrderField(), coursePageQueryDTO.getOrderAsc());
|
||||
//
|
||||
// // 设置分页参数
|
||||
// int pageIndex = coursePageQueryDTO.getPageIndex() != null ? coursePageQueryDTO.getPageIndex() : 0;
|
||||
// int pageSize = coursePageQueryDTO.getPageSize() != null ? coursePageQueryDTO.getPageSize() : 10;
|
||||
// query.setPageIndex(pageIndex);
|
||||
// query.setPageSize(pageSize);
|
||||
//
|
||||
// // 执行查询
|
||||
// PageList<Course> coursePageList = courseDao.findPage(query.builder());
|
||||
//
|
||||
// // 转换为CoursePageVo
|
||||
// PageList<CoursePageVo> result = new PageList<>();
|
||||
// result.setCount(coursePageList.getCount());
|
||||
// result.setPageSize(coursePageList.getPageSize());
|
||||
//
|
||||
// // 子查询:根据课程id查询课程下的教师
|
||||
// List<String> courseIds = coursePageList.getList().stream()
|
||||
// .map(Course::getId)
|
||||
// .collect(Collectors.toList());
|
||||
// List<CourseTeacher> courseTeacherList = getCourseTeacherList(courseIds);
|
||||
// List<CoursePageVo> voList = coursePageList.getList().stream()
|
||||
// .map(c -> convertToVo(c, courseTeacherList))
|
||||
// .collect(Collectors.toList());
|
||||
// result.setList(voList);
|
||||
//
|
||||
// return result;
|
||||
// 第二版
|
||||
List<String> readIds = userOrgIds.getReadIds();
|
||||
orgIds.addAll(readIds);
|
||||
|
||||
long total = courseDao.countCourse(coursePageQueryDTO, isSystemAdmin, orgIds, currentAccountId, mySqlSchemaProperties.getUserCenterSchema());
|
||||
List<CoursePageVo> voList = courseDao.queryCourse(coursePageQueryDTO, isSystemAdmin, orgIds, currentAccountId, true, mySqlSchemaProperties.getUserCenterSchema());
|
||||
PageList<CoursePageVo> result = new PageList<>();
|
||||
@@ -306,7 +250,7 @@ public class CoursePageServiceImpl implements ICoursePageService {
|
||||
map.put("published", coursePageVo.getPublished() == null || !coursePageVo.getPublished() ? "未发布" : "已发布");
|
||||
map.put("enabled", coursePageVo.getEnabled() == null || coursePageVo.getEnabled() ? "停用" : "启用");
|
||||
map.put("openCourse", coursePageVo.getOpenCourse() == null || coursePageVo.getOpenCourse() == 0 ? "否" : "是");
|
||||
map.put("orgName", coursePageVo.getOrgName());
|
||||
map.put("orgName", coursePageVo.getOrgFullName()); // 修改为组织全名
|
||||
map.put("sysCreateBy", coursePageVo.getSysCreateBy());
|
||||
map.put("createFrom", CourseCreateFromEnum.getByCode(coursePageVo.getCreateFrom()).getLabel());
|
||||
map.put("sysCreateTime", formatter.format(coursePageVo.getSysCreateTime()));
|
||||
|
||||
@@ -0,0 +1,435 @@
|
||||
package com.xboe.module.course.service.impl;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.xboe.common.OrderCondition;
|
||||
import com.xboe.core.orm.FieldFilters;
|
||||
import com.xboe.module.course.dao.CourseFileDao;
|
||||
import com.xboe.module.course.dto.CourseResourceUploadDto;
|
||||
import com.xboe.module.course.entity.CourseFile;
|
||||
import com.xboe.module.course.service.CourseContentValidationService;
|
||||
import com.xboe.module.course.service.CourseResourceService;
|
||||
|
||||
import com.xboe.module.util.FileUploadUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class CourseResourceServiceImpl implements CourseResourceService {
|
||||
|
||||
@Resource
|
||||
private CourseFileDao courseFileDao;
|
||||
|
||||
@Resource
|
||||
private FileUploadUtil fileUploadUtil;
|
||||
|
||||
@Resource
|
||||
private CourseContentValidationService courseContentValidationService;
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
// 资源类型映射
|
||||
private static final Map<Integer, String[]> RESOURCE_TYPE_EXTENSIONS = new HashMap<>();
|
||||
private static final Map<Integer, String> RESOURCE_TYPE_NAMES = new HashMap<>();
|
||||
|
||||
static {
|
||||
// 文件扩展名映射
|
||||
RESOURCE_TYPE_EXTENSIONS.put(10, new String[]{"mp4"}); // 视频
|
||||
RESOURCE_TYPE_EXTENSIONS.put(20, new String[]{"mp3"}); // 音频
|
||||
RESOURCE_TYPE_EXTENSIONS.put(40, new String[]{"doc", "docx", "xls", "xlsx", "pptx", "txt", "pdf"}); // 文档
|
||||
RESOURCE_TYPE_EXTENSIONS.put(50, new String[]{"zip"}); // SCORM包
|
||||
|
||||
// 资源类型名称映射
|
||||
RESOURCE_TYPE_NAMES.put(10, "视频");
|
||||
RESOURCE_TYPE_NAMES.put(20, "音频");
|
||||
RESOURCE_TYPE_NAMES.put(40, "文档");
|
||||
RESOURCE_TYPE_NAMES.put(41, "图文");
|
||||
RESOURCE_TYPE_NAMES.put(50, "SCORM包");
|
||||
RESOURCE_TYPE_NAMES.put(90, "外部链接");
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void processCourseResources(String courseId,
|
||||
List<CourseResourceUploadDto.CourseFileResource> resources,
|
||||
String orgId, String orgName,
|
||||
String resOwner1, String resOwner2, String resOwner3,
|
||||
String ownership1, String ownership2, String ownership3) {
|
||||
|
||||
if (resources == null || resources.isEmpty()) {
|
||||
log.info("课程ID={} 没有需要处理的资源", courseId);
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("开始处理课程资源,课程ID={},资源数量={}", courseId, resources.size());
|
||||
|
||||
int successCount = 0;
|
||||
int failCount = 0;
|
||||
|
||||
for (CourseResourceUploadDto.CourseFileResource resource : resources) {
|
||||
try {
|
||||
processSingleResource(courseId, resource, orgId, orgName,
|
||||
resOwner1, resOwner2, resOwner3,
|
||||
ownership1, ownership2, ownership3);
|
||||
successCount++;
|
||||
log.info("处理资源成功: 课程ID={}, 资源名称={}, 类型={}",
|
||||
courseId, resource.getName(), resource.getResType());
|
||||
} catch (Exception e) {
|
||||
failCount++;
|
||||
log.error("处理资源失败: 课程ID={}, 资源名称={}, 类型={}, 错误: {}",
|
||||
courseId, resource.getName(), resource.getResType(), e.getMessage(), e);
|
||||
// 继续处理其他资源
|
||||
}
|
||||
}
|
||||
|
||||
log.info("资源处理完成: 课程ID={}, 成功={}, 失败={}", courseId, successCount, failCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理单个资源
|
||||
*/
|
||||
private void processSingleResource(String courseId,
|
||||
CourseResourceUploadDto.CourseFileResource resource,
|
||||
String orgId, String orgName,
|
||||
String resOwner1, String resOwner2, String resOwner3,
|
||||
String ownership1, String ownership2, String ownership3) throws IOException {
|
||||
|
||||
// 验证资源
|
||||
validateResource(resource);
|
||||
|
||||
// 创建CourseFile实体
|
||||
CourseFile courseFile = new CourseFile();
|
||||
courseFile.setName(resource.getName());
|
||||
courseFile.setCourseId(courseId);
|
||||
courseFile.setOrgId(orgId);
|
||||
courseFile.setOrgName(orgName);
|
||||
courseFile.setResType(resource.getResType());
|
||||
courseFile.setResOwner1(resOwner1);
|
||||
courseFile.setResOwner2(resOwner2);
|
||||
courseFile.setResOwner3(resOwner3);
|
||||
courseFile.setOwnership1(ownership1);
|
||||
courseFile.setOwnership2(ownership2);
|
||||
courseFile.setOwnership3(ownership3);
|
||||
courseFile.setDown(resource.getDown() != null ? resource.getDown() : false);
|
||||
courseFile.setRemark(resource.getRemark());
|
||||
courseFile.setDevice(resource.getDevice() != null ? resource.getDevice() : 3); // 默认多端可见
|
||||
courseFile.setDecoder(resource.getDecoder());
|
||||
courseFile.setVideoWidth(resource.getVideoWidth());
|
||||
courseFile.setVideoHeight(resource.getVideoHeight());
|
||||
courseFile.setConverStatus(resource.getConverStatus() != null ? resource.getConverStatus() : 0);
|
||||
courseFile.setConverError(resource.getConverError());
|
||||
|
||||
// 根据资源类型处理
|
||||
Integer resType = resource.getResType();
|
||||
if (resType == 10 || resType == 20 || resType == 40 || resType == 50) {
|
||||
// 文件资源(视频、音频、文档、SCORM包)
|
||||
processFileResource(courseFile, resource);
|
||||
} else if (resType == 41) {
|
||||
// 图文资源
|
||||
processGraphicTextResource(courseFile, resource);
|
||||
} else if (resType == 90) {
|
||||
// 外部链接资源
|
||||
processExternalLinkResource(courseFile, resource);
|
||||
} else {
|
||||
throw new IllegalArgumentException("不支持的资源类型: " + resType);
|
||||
}
|
||||
|
||||
// 保存到数据库
|
||||
courseFileDao.save(courseFile);
|
||||
log.info("资源保存到数据库成功: ID={}, 名称={}, 类型={}",
|
||||
courseFile.getId(), courseFile.getName(), courseFile.getResType());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理文件资源(视频、音频、文档、SCORM包通用方法)
|
||||
*/
|
||||
private void processFileResource(CourseFile courseFile,
|
||||
CourseResourceUploadDto.CourseFileResource resource) throws IOException {
|
||||
|
||||
Integer resType = resource.getResType();
|
||||
String resourceTypeName = RESOURCE_TYPE_NAMES.get(resType);
|
||||
String[] allowedExtensions = RESOURCE_TYPE_EXTENSIONS.get(resType);
|
||||
|
||||
// 验证文件
|
||||
if (StringUtils.isBlank(resource.getFileBase64())) {
|
||||
throw new IllegalArgumentException(resourceTypeName + "文件内容不能为空");
|
||||
}
|
||||
|
||||
String originalFileName = StringUtils.isNotBlank(resource.getOriginalFileName())
|
||||
? resource.getOriginalFileName()
|
||||
: getDefaultFileName(resType);
|
||||
|
||||
// 验证文件类型
|
||||
if (!fileUploadUtil.validateFileType(originalFileName, allowedExtensions)) {
|
||||
throw new IllegalArgumentException(resourceTypeName + "文件只支持: " +
|
||||
String.join(", ", allowedExtensions));
|
||||
}
|
||||
|
||||
// 验证文件大小
|
||||
validateFileSize(resource.getFileBase64(), resType);
|
||||
|
||||
// 保存文件
|
||||
FileUploadUtil.FileSaveResult saveResult = fileUploadUtil.saveBase64File(
|
||||
resource.getFileBase64(), originalFileName);
|
||||
|
||||
// 设置文件信息
|
||||
courseFile.setFileName(saveResult.getFileName());
|
||||
courseFile.setFileType(fileUploadUtil.getFileExtension(originalFileName));
|
||||
courseFile.setFileSize((int) (saveResult.getFileSize() / 1024)); // KB
|
||||
courseFile.setFilePath(saveResult.getFilePath());
|
||||
courseFile.setPreviewFilePath(saveResult.getFileUrl());
|
||||
|
||||
// 设置时长(视频/音频)
|
||||
if (resType == 10 || resType == 20) {
|
||||
courseFile.setDuration(resource.getDuration() != null ? resource.getDuration() : 0);
|
||||
}
|
||||
|
||||
// 视频特定设置
|
||||
if (resType == 10) {
|
||||
courseFile.setDecoder(StringUtils.isNotBlank(resource.getDecoder()) ? resource.getDecoder() : "h264");
|
||||
courseFile.setConverStatus(0); // 视频可能需要转码
|
||||
} else {
|
||||
courseFile.setConverStatus(2); // 其他文件不需要转化
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理图文资源
|
||||
*/
|
||||
private void processGraphicTextResource(CourseFile courseFile,
|
||||
CourseResourceUploadDto.CourseFileResource resource) throws IOException {
|
||||
|
||||
// 验证图文内容
|
||||
if (resource.getGraphicText() == null) {
|
||||
throw new IllegalArgumentException("图文内容不能为空");
|
||||
}
|
||||
|
||||
CourseResourceUploadDto.GraphicTextContent graphicText = resource.getGraphicText();
|
||||
if (StringUtils.isBlank(graphicText.getTitle())) {
|
||||
throw new IllegalArgumentException("图文标题不能为空");
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(graphicText.getContent())) {
|
||||
throw new IllegalArgumentException("图文内容不能为空");
|
||||
}
|
||||
|
||||
// 处理图片(如果有)
|
||||
String imageUrl = null;
|
||||
String imagePath = null;
|
||||
if (StringUtils.isNotBlank(graphicText.getImageBase64())) {
|
||||
try {
|
||||
String imageName = StringUtils.isNotBlank(graphicText.getImageName())
|
||||
? graphicText.getImageName()
|
||||
: "image_" + System.currentTimeMillis() + ".jpg";
|
||||
|
||||
FileUploadUtil.FileSaveResult imageResult = fileUploadUtil.saveBase64File(
|
||||
graphicText.getImageBase64(), imageName);
|
||||
imageUrl = imageResult.getFileUrl();
|
||||
imagePath = imageResult.getFilePath();
|
||||
} catch (Exception e) {
|
||||
log.warn("处理图文图片失败,继续处理其他内容", e);
|
||||
}
|
||||
}
|
||||
|
||||
// 构建图文内容JSON
|
||||
Map<String, Object> contentMap = new HashMap<>();
|
||||
contentMap.put("title", graphicText.getTitle());
|
||||
contentMap.put("content", graphicText.getContent());
|
||||
contentMap.put("format", graphicText.getFormat());
|
||||
contentMap.put("fontSize", graphicText.getFontSize());
|
||||
contentMap.put("lineHeight", graphicText.getLineHeight());
|
||||
contentMap.put("textAlign", graphicText.getTextAlign());
|
||||
contentMap.put("imageUrl", imageUrl);
|
||||
contentMap.put("imageName", graphicText.getImageName());
|
||||
|
||||
String contentJson = objectMapper.writeValueAsString(contentMap);
|
||||
|
||||
// 设置文件信息
|
||||
courseFile.setFileName("graphic_text.json");
|
||||
courseFile.setFileType("json");
|
||||
courseFile.setFileSize((int) (contentJson.length() / 1024)); // KB
|
||||
courseFile.setContent(contentJson);
|
||||
if (imageUrl != null) {
|
||||
courseFile.setPreviewFilePath(imageUrl);
|
||||
}
|
||||
if (imagePath != null) {
|
||||
courseFile.setFilePath(imagePath);
|
||||
} else {
|
||||
courseFile.setFilePath("graphic_text/" + courseFile.getId() + ".json");
|
||||
}
|
||||
courseFile.setConverStatus(2); // 图文不需要转化
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理外部链接资源
|
||||
*/
|
||||
private void processExternalLinkResource(CourseFile courseFile,
|
||||
CourseResourceUploadDto.CourseFileResource resource) throws IOException {
|
||||
|
||||
// 验证链接内容
|
||||
if (resource.getExternalLink() == null) {
|
||||
throw new IllegalArgumentException("外部链接内容不能为空");
|
||||
}
|
||||
|
||||
CourseResourceUploadDto.ExternalLinkContent externalLink = resource.getExternalLink();
|
||||
if (StringUtils.isBlank(externalLink.getUrl())) {
|
||||
throw new IllegalArgumentException("链接地址不能为空");
|
||||
}
|
||||
|
||||
// 构建链接内容JSON
|
||||
Map<String, Object> contentMap = new HashMap<>();
|
||||
contentMap.put("url", externalLink.getUrl());
|
||||
contentMap.put("openType", externalLink.getOpenType() != null ? externalLink.getOpenType() : 2);
|
||||
contentMap.put("title", externalLink.getTitle());
|
||||
contentMap.put("description", externalLink.getDescription());
|
||||
|
||||
String contentJson = objectMapper.writeValueAsString(contentMap);
|
||||
|
||||
// 设置文件信息
|
||||
courseFile.setFileName("external_link.json");
|
||||
courseFile.setFileType("json");
|
||||
courseFile.setFileSize((int) (contentJson.length() / 1024)); // KB
|
||||
courseFile.setContent(contentJson);
|
||||
courseFile.setFilePath("external_link/" + courseFile.getId() + ".json");
|
||||
courseFile.setConverStatus(2); // 链接不需要转化
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证资源
|
||||
*/
|
||||
private void validateResource(CourseResourceUploadDto.CourseFileResource resource) {
|
||||
if (resource == null) {
|
||||
throw new IllegalArgumentException("资源不能为空");
|
||||
}
|
||||
|
||||
if (resource.getResType() == null) {
|
||||
throw new IllegalArgumentException("资源类型不能为空");
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(resource.getName())) {
|
||||
throw new IllegalArgumentException("资源名称不能为空");
|
||||
}
|
||||
|
||||
// 使用服务层的验证方法替代DTO中的静态方法
|
||||
String validationResult = courseContentValidationService.validateResource(resource);
|
||||
if (validationResult != null) {
|
||||
throw new IllegalArgumentException(validationResult);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证文件大小
|
||||
*/
|
||||
private void validateFileSize(String base64Content, Integer resType) {
|
||||
if (StringUtils.isBlank(base64Content)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 估算文件大小(Base64编码比原始大约33%)
|
||||
int base64Length = base64Content.length();
|
||||
if (base64Content.contains(",")) {
|
||||
base64Length = base64Content.split(",")[1].length();
|
||||
}
|
||||
|
||||
// Base64解码后的实际大小约为 base64Length * 3 / 4
|
||||
long estimatedSize = (long) base64Length * 3 / 4;
|
||||
|
||||
// 根据类型设置限制
|
||||
long maxSize;
|
||||
switch (resType) {
|
||||
case 10: // 视频
|
||||
case 40: // 文档
|
||||
case 50: // SCORM包
|
||||
maxSize = 1024L * 1024 * 1024; // 1GB
|
||||
break;
|
||||
case 20: // 音频
|
||||
maxSize = 16L * 1024 * 1024; // 16MB
|
||||
break;
|
||||
default:
|
||||
maxSize = 100L * 1024 * 1024; // 100MB 默认
|
||||
}
|
||||
|
||||
if (estimatedSize > maxSize) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"文件大小超过限制,估计大小: %.2fMB, 最大限制: %.2fMB",
|
||||
estimatedSize / (1024.0 * 1024.0),
|
||||
maxSize / (1024.0 * 1024.0)));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("验证文件大小失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认文件名
|
||||
*/
|
||||
private String getDefaultFileName(Integer resType) {
|
||||
switch (resType) {
|
||||
case 10: return "video.mp4";
|
||||
case 20: return "audio.mp3";
|
||||
case 40: return "document.pdf";
|
||||
case 50: return "scorm.zip";
|
||||
default: return "file.bin";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CourseFile> getCourseResources(String courseId) {
|
||||
try {
|
||||
List<CourseFile> resources = courseFileDao.findList(
|
||||
OrderCondition.asc("sysCreateTime"),
|
||||
FieldFilters.eq("courseId", courseId),
|
||||
FieldFilters.eq("deleted", false)
|
||||
);
|
||||
log.info("获取课程资源成功: courseId={}, 数量={}", courseId, resources.size());
|
||||
return resources;
|
||||
} catch (Exception e) {
|
||||
log.error("获取课程资源失败: courseId={}", courseId, e);
|
||||
throw new RuntimeException("获取课程资源失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void deleteCourseResources(String courseId) {
|
||||
try {
|
||||
List<CourseFile> resources = getCourseResources(courseId);
|
||||
for (CourseFile resource : resources) {
|
||||
deleteResource(resource.getId());
|
||||
}
|
||||
log.info("删除课程资源成功: courseId={}, 数量={}", courseId, resources.size());
|
||||
} catch (Exception e) {
|
||||
log.error("删除课程资源失败: courseId={}", courseId, e);
|
||||
throw new RuntimeException("删除课程资源失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void deleteResource(String resourceId) {
|
||||
try {
|
||||
CourseFile resource = courseFileDao.get(resourceId);
|
||||
if (resource != null) {
|
||||
// 标记为删除
|
||||
resource.setDeleted(true);
|
||||
courseFileDao.update(resource);
|
||||
|
||||
// 删除物理文件
|
||||
if (StringUtils.isNotBlank(resource.getFilePath())) {
|
||||
fileUploadUtil.deleteFile(resource.getFilePath());
|
||||
}
|
||||
log.info("删除资源成功: resourceId={}, 名称={}", resourceId, resource.getName());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("删除资源失败: resourceId={}", resourceId, e);
|
||||
throw new RuntimeException("删除资源失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
package com.xboe.module.course.service.impl;
|
||||
|
||||
import com.xboe.common.OrderCondition;
|
||||
import com.xboe.core.orm.FieldFilters;
|
||||
import com.xboe.module.course.vo.TypeTreeVo;
|
||||
import com.xboe.module.course.service.TypeTreeService;
|
||||
import com.xboe.module.type.dao.TypeDao;
|
||||
import com.xboe.module.type.entity.Type;
|
||||
import com.xboe.system.organization.dao.OrganizationDao;
|
||||
import com.xboe.system.organization.entity.Organization;
|
||||
import com.xboe.system.organization.vo.OrganizationVo;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class TypeTreeServiceImpl implements TypeTreeService {
|
||||
|
||||
@Resource
|
||||
private TypeDao typeDao;
|
||||
|
||||
@Resource
|
||||
private OrganizationDao organizationDao;
|
||||
|
||||
@Override
|
||||
public List<TypeTreeVo> getCourseTypeTree() {
|
||||
try {
|
||||
// 查询课程相关的分类(直接用数据库列名sys_res_type)
|
||||
List<Type> allTypes = typeDao.findList(
|
||||
OrderCondition.asc("orderIndex"),
|
||||
FieldFilters.eq("sys_res_type", 1), // 关键:用数据库原生列名
|
||||
FieldFilters.eq("status", 1),
|
||||
FieldFilters.eq("deleted", false)
|
||||
);
|
||||
|
||||
return buildTypeTree(allTypes);
|
||||
} catch (Exception e) {
|
||||
log.error("获取课程分类树失败", e);
|
||||
throw new RuntimeException("获取课程分类树失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OrganizationVo> getResourceOwnerTree() {
|
||||
try {
|
||||
// 查询组织机构树
|
||||
List<Organization> allOrgs = organizationDao.findList(
|
||||
OrderCondition.asc("code"),
|
||||
FieldFilters.eq("status", 1),
|
||||
FieldFilters.eq("deleted", false)
|
||||
);
|
||||
|
||||
return buildOrgTree(allOrgs);
|
||||
} catch (Exception e) {
|
||||
log.error("获取资源归属树失败", e);
|
||||
throw new RuntimeException("获取资源归属树失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TypeTreeVo> getChildTypes(String parentId) {
|
||||
try {
|
||||
List<Type> childTypes = typeDao.findList(
|
||||
OrderCondition.asc("orderIndex"),
|
||||
FieldFilters.eq("parent_id", parentId), // 关键:用数据库原生列名
|
||||
FieldFilters.eq("status", 1),
|
||||
FieldFilters.eq("deleted", false),
|
||||
FieldFilters.eq("sys_res_type", 1) // 关键:用数据库原生列名
|
||||
);
|
||||
|
||||
return childTypes.stream().map(this::convertTypeToTreeVo).collect(Collectors.toList());
|
||||
} catch (Exception e) {
|
||||
log.error("获取子分类失败,parentId={}", parentId, e);
|
||||
throw new RuntimeException("获取子分类失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OrganizationVo> getChildOrgs(String parentId) {
|
||||
try {
|
||||
List<Organization> childOrgs = organizationDao.findList(
|
||||
OrderCondition.asc("code"),
|
||||
FieldFilters.eq("parentId", parentId),
|
||||
FieldFilters.eq("status", 1),
|
||||
FieldFilters.eq("deleted", false)
|
||||
);
|
||||
|
||||
return childOrgs.stream().map(this::convertOrgToVo).collect(Collectors.toList());
|
||||
} catch (Exception e) {
|
||||
log.error("获取子组织失败,parentId={}", parentId, e);
|
||||
throw new RuntimeException("获取子组织失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateTypeHierarchy(String childId, String parentId) {
|
||||
try {
|
||||
// 1. 非空校验
|
||||
if (StringUtils.isBlank(childId) || StringUtils.isBlank(parentId)) {
|
||||
log.warn("分类层级校验失败:子分类ID[{}]或父分类ID[{}]为空", childId, parentId);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 确认传入的ID
|
||||
log.info("开始校验分类层级:子分类ID={}, 父分类ID={}", childId, parentId);
|
||||
|
||||
// 2. 查询子分类
|
||||
List<Type> childTypeList = typeDao.findList(
|
||||
FieldFilters.eq("id", childId),
|
||||
FieldFilters.eq("sys_res_type", 1), // 数据库列名:sys_res_type
|
||||
FieldFilters.eq("status", 1),
|
||||
FieldFilters.eq("deleted", false)
|
||||
);
|
||||
if (childTypeList == null || childTypeList.isEmpty()) {
|
||||
log.warn("分类层级校验失败:子分类ID={} 不存在(或非课程类型/未启用/已删除)", childId);
|
||||
// 查询所有课程类型分类
|
||||
List<Type> allCourseTypes = typeDao.findList(FieldFilters.eq("sys_res_type", 1));
|
||||
log.info("当前数据库中课程类型分类列表:{}",
|
||||
allCourseTypes.stream().map(Type::getId).collect(Collectors.toList()));
|
||||
return false;
|
||||
}
|
||||
Type childType = childTypeList.get(0);
|
||||
log.info("查询到子分类信息:ID={}, 父ID={}, 资源类型={}, 状态={}, 删除标识={}",
|
||||
childType.getId(), childType.getParentId(), childType.getSysResType(),
|
||||
childType.getStatus(), childType.getDeleted());
|
||||
|
||||
// 3. 查询父分类
|
||||
List<Type> parentTypeList = typeDao.findList(
|
||||
FieldFilters.eq("id", parentId),
|
||||
FieldFilters.eq("sys_res_type", 1), // 数据库列名:sys_res_type
|
||||
FieldFilters.eq("status", 1),
|
||||
FieldFilters.eq("deleted", false)
|
||||
);
|
||||
if (parentTypeList == null || parentTypeList.isEmpty()) {
|
||||
log.warn("分类层级校验失败:父分类ID={} 不存在(或非课程类型/未启用/已删除)", parentId);
|
||||
return false;
|
||||
}
|
||||
Type parentType = parentTypeList.get(0);
|
||||
log.info("查询到父分类信息:ID={}, 父ID={}, 资源类型={}, 状态={}, 删除标识={}",
|
||||
parentType.getId(), parentType.getParentId(), parentType.getSysResType(),
|
||||
parentType.getStatus(), parentType.getDeleted());
|
||||
|
||||
// 4. 验证父分类是一级分类
|
||||
if (!StringUtils.isBlank(parentType.getParentId())) {
|
||||
String parentParentId = parentType.getParentId();
|
||||
// 一级分类
|
||||
boolean isRootLevel = "0".equals(parentParentId) || "-1".equals(parentParentId);
|
||||
if (!isRootLevel) {
|
||||
log.warn("分类层级校验失败:父分类ID={} 不是一级分类(其parentId={})",
|
||||
parentId, parentType.getParentId());
|
||||
return false;
|
||||
}
|
||||
log.info("父分类是一级分类:parentId={}, parentParentId={}", parentId, parentParentId);
|
||||
} else {
|
||||
log.info("父分类是一级分类:parentId={}, parentParentId为空", parentId);
|
||||
}
|
||||
|
||||
// 5. 验证
|
||||
boolean isParentMatch = parentId.equals(childType.getParentId());
|
||||
if (!isParentMatch) {
|
||||
log.warn("分类层级校验失败:子分类ID={} 的父ID={} 与传入的父ID={} 不匹配",
|
||||
childId, childType.getParentId(), parentId);
|
||||
}
|
||||
|
||||
log.info("分类层级校验完成:子分类ID={}, 父分类ID={}, 校验结果={}", childId, parentId, isParentMatch);
|
||||
return isParentMatch;
|
||||
} catch (Exception e) {
|
||||
log.error("验证分类层级关系异常, childId={}, parentId={}", childId, parentId, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建分类树
|
||||
*/
|
||||
private List<TypeTreeVo> buildTypeTree(List<Type> allTypes) {
|
||||
Map<String, TypeTreeVo> voMap = new HashMap<>();
|
||||
List<TypeTreeVo> rootNodes = new ArrayList<>();
|
||||
|
||||
// 第一遍:创建所有节点
|
||||
for (Type type : allTypes) {
|
||||
TypeTreeVo vo = convertTypeToTreeVo(type);
|
||||
voMap.put(type.getId(), vo);
|
||||
}
|
||||
|
||||
// 第二遍:建立父子关系
|
||||
for (Type type : allTypes) {
|
||||
TypeTreeVo vo = voMap.get(type.getId());
|
||||
if (StringUtils.isBlank(type.getParentId()) ||
|
||||
"0".equals(type.getParentId()) ||
|
||||
"-1".equals(type.getParentId())) {
|
||||
rootNodes.add(vo);
|
||||
} else {
|
||||
TypeTreeVo parent = voMap.get(type.getParentId());
|
||||
if (parent != null) {
|
||||
if (parent.getChildren() == null) {
|
||||
parent.setChildren(new ArrayList<>());
|
||||
}
|
||||
parent.getChildren().add(vo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rootNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建组织机构树
|
||||
*/
|
||||
private List<OrganizationVo> buildOrgTree(List<Organization> allOrgs) {
|
||||
Map<String, OrganizationVo> voMap = new HashMap<>();
|
||||
List<OrganizationVo> rootNodes = new ArrayList<>();
|
||||
|
||||
// 第一遍:创建所有节点
|
||||
for (Organization org : allOrgs) {
|
||||
OrganizationVo vo = convertOrgToVo(org);
|
||||
voMap.put(org.getId(), vo);
|
||||
}
|
||||
|
||||
// 第二遍:建立父子关系
|
||||
for (Organization org : allOrgs) {
|
||||
OrganizationVo vo = voMap.get(org.getId());
|
||||
if (StringUtils.isBlank(org.getParentId())) {
|
||||
rootNodes.add(vo);
|
||||
} else {
|
||||
OrganizationVo parent = voMap.get(org.getParentId());
|
||||
if (parent != null) {
|
||||
if (parent.getChildren() == null) {
|
||||
parent.setChildren(new ArrayList<>());
|
||||
}
|
||||
parent.getChildren().add(vo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rootNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转TypeTreeVo
|
||||
*/
|
||||
private TypeTreeVo convertTypeToTreeVo(Type type) {
|
||||
TypeTreeVo vo = new TypeTreeVo();
|
||||
vo.setValue(type.getId());
|
||||
vo.setLabel(type.getName());
|
||||
vo.setParentId(type.getParentId());
|
||||
vo.setOrderIndex(type.getOrderIndex());
|
||||
vo.setStatus(type.getStatus());
|
||||
vo.setSysResType(type.getSysResType());
|
||||
return vo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转OrganizationVo
|
||||
*/
|
||||
private OrganizationVo convertOrgToVo(Organization org) {
|
||||
OrganizationVo vo = new OrganizationVo();
|
||||
vo.setId(org.getId());
|
||||
vo.setName(org.getName());
|
||||
vo.setCode(org.getCode());
|
||||
vo.setParentId(org.getParentId());
|
||||
vo.setStatus(org.getStatus());
|
||||
vo.setNamePath(org.getNamePath());
|
||||
vo.setDescription(org.getDescription());
|
||||
return vo;
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,11 @@ public class CoursePageVo {
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 课程类型
|
||||
*/
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
* 课程封面图片地址
|
||||
*/
|
||||
@@ -88,6 +93,13 @@ public class CoursePageVo {
|
||||
@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;
|
||||
|
||||
/**
|
||||
* 目标人群
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.xboe.module.course.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 分类树VO(用于前端级联选择器)
|
||||
*/
|
||||
@Data
|
||||
public class TypeTreeVo {
|
||||
private String value;
|
||||
private String label;
|
||||
private String parentId;
|
||||
private Integer orderIndex;
|
||||
private Integer status;
|
||||
private Integer sysResType;
|
||||
private List<TypeTreeVo> children;
|
||||
|
||||
public TypeTreeVo() {}
|
||||
|
||||
public TypeTreeVo(String value, String label, String parentId) {
|
||||
this.value = value;
|
||||
this.label = label;
|
||||
this.parentId = parentId;
|
||||
}
|
||||
}
|
||||
@@ -333,7 +333,7 @@ public class TeacherApi extends ApiBaseController {
|
||||
OutputStream = response.getOutputStream();
|
||||
LinkedHashMap<String,String> map = new LinkedHashMap<>();
|
||||
map.put("姓名","name");
|
||||
map.put("工号","userNo");
|
||||
map.put("工号","userNo"); // 重点
|
||||
map.put("部门","departName");
|
||||
map.put("创建时间","sysCreateTime");
|
||||
map.put("修改时间","sysUpdateTime");
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
package com.xboe.module.util;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Base64;
|
||||
import java.util.UUID;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class FileUploadUtil {
|
||||
|
||||
@Value("${xboe.upload.file.save_path:D:/file_save}")
|
||||
private String uploadSavePath;
|
||||
|
||||
@Value("${xboe.upload.file.http_path:http://192.168.0.253/upload}")
|
||||
private String uploadHttpPath;
|
||||
|
||||
@Value("${xboe.upload.file.temp_path:D:/temp}")
|
||||
private String uploadTempPath;
|
||||
|
||||
/**
|
||||
* 保存Base64文件到磁盘
|
||||
*/
|
||||
public FileSaveResult saveBase64File(String base64Content, String originalFileName) throws IOException {
|
||||
if (StringUtils.isBlank(base64Content)) {
|
||||
throw new IllegalArgumentException("文件内容不能为空");
|
||||
}
|
||||
|
||||
log.info("开始保存文件,原始文件名: {}, 保存路径: {}", originalFileName, uploadSavePath);
|
||||
|
||||
// 解码Base64
|
||||
byte[] fileBytes;
|
||||
try {
|
||||
if (base64Content.contains(",")) {
|
||||
// 去除data:image/png;base64,前缀
|
||||
base64Content = base64Content.split(",")[1];
|
||||
}
|
||||
fileBytes = Base64.getDecoder().decode(base64Content);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IOException("Base64解码失败: " + e.getMessage());
|
||||
}
|
||||
|
||||
// 生成文件名
|
||||
String fileExtension = getFileExtension(originalFileName);
|
||||
String uuid = UUID.randomUUID().toString().replace("-", "");
|
||||
String fileName = uuid + (StringUtils.isNotBlank(fileExtension) ? "." + fileExtension : "");
|
||||
|
||||
// 按日期组织目录
|
||||
String datePath = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
|
||||
String relativePath = datePath + "/" + fileName;
|
||||
|
||||
// 处理Windows路径分隔符
|
||||
Path fullPath = Paths.get(uploadSavePath, datePath, fileName);
|
||||
|
||||
// 创建目录
|
||||
Path directory = Paths.get(uploadSavePath, datePath);
|
||||
if (!Files.exists(directory)) {
|
||||
Files.createDirectories(directory);
|
||||
log.info("创建目录: {}", directory.toString());
|
||||
}
|
||||
|
||||
// 保存文件
|
||||
try (FileOutputStream fos = new FileOutputStream(fullPath.toFile())) {
|
||||
fos.write(fileBytes);
|
||||
}
|
||||
|
||||
// 构建访问URL
|
||||
String fileUrl = uploadHttpPath + "/" + relativePath.replace("\\", "/");
|
||||
|
||||
FileSaveResult result = new FileSaveResult();
|
||||
result.setFilePath(relativePath); // 相对路径,用于数据库存储
|
||||
result.setFileUrl(fileUrl); // 完整的HTTP访问URL
|
||||
result.setFileName(fileName); // 保存后的文件名
|
||||
result.setOriginalFileName(originalFileName); // 原始文件名
|
||||
result.setFileSize(fileBytes.length); // 文件大小(字节)
|
||||
|
||||
log.info("文件保存成功: 原始文件={}, 保存文件={}, 大小={}字节, 路径={}",
|
||||
originalFileName, fileName, fileBytes.length, relativePath);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件扩展名
|
||||
*/
|
||||
public String getFileExtension(String filename) {
|
||||
if (StringUtils.isBlank(filename) || !filename.contains(".")) {
|
||||
return "";
|
||||
}
|
||||
return filename.substring(filename.lastIndexOf(".") + 1).toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证文件类型
|
||||
*/
|
||||
public boolean validateFileType(String fileName, String[] allowedExtensions) {
|
||||
String extension = getFileExtension(fileName);
|
||||
if (StringUtils.isBlank(extension)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (String allowed : allowedExtensions) {
|
||||
if (allowed.equalsIgnoreCase(extension)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取支持的文档类型
|
||||
*/
|
||||
public String[] getDocumentExtensions() {
|
||||
return new String[]{"doc", "docx", "xls", "xlsx", "pptx", "txt", "pdf"};
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
*/
|
||||
public boolean deleteFile(String filePath) {
|
||||
if (StringUtils.isBlank(filePath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
Path fullPath = Paths.get(uploadSavePath, filePath);
|
||||
File file = fullPath.toFile();
|
||||
if (file.exists()) {
|
||||
boolean deleted = file.delete();
|
||||
log.info("删除文件: {}, 结果: {}", fullPath.toString(), deleted);
|
||||
return deleted;
|
||||
}
|
||||
log.warn("文件不存在,无法删除: {}", fullPath.toString());
|
||||
return false;
|
||||
} catch (Exception e) {
|
||||
log.error("删除文件失败: {}", filePath, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件完整路径
|
||||
*/
|
||||
public String getFullPath(String relativePath) {
|
||||
if (StringUtils.isBlank(relativePath)) {
|
||||
return "";
|
||||
}
|
||||
return Paths.get(uploadSavePath, relativePath).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查文件是否存在
|
||||
*/
|
||||
public boolean fileExists(String relativePath) {
|
||||
if (StringUtils.isBlank(relativePath)) {
|
||||
return false;
|
||||
}
|
||||
String fullPath = getFullPath(relativePath);
|
||||
File file = new File(fullPath);
|
||||
return file.exists();
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
try {
|
||||
// 确保上传目录存在
|
||||
File saveDir = new File(uploadSavePath);
|
||||
if (!saveDir.exists()) {
|
||||
boolean created = saveDir.mkdirs();
|
||||
log.info("创建上传目录: {}, 结果: {}", uploadSavePath, created);
|
||||
} else {
|
||||
log.info("上传目录已存在: {}", uploadSavePath);
|
||||
}
|
||||
|
||||
// 确保临时目录存在
|
||||
File tempDir = new File(uploadTempPath);
|
||||
if (!tempDir.exists()) {
|
||||
boolean created = tempDir.mkdirs();
|
||||
log.info("创建临时目录: {}, 结果: {}", uploadTempPath, created);
|
||||
}
|
||||
|
||||
log.info("文件上传配置初始化完成: savePath={}, httpPath={}, tempPath={}",
|
||||
uploadSavePath, uploadHttpPath, uploadTempPath);
|
||||
|
||||
// 检查目录权限
|
||||
if (!saveDir.canWrite()) {
|
||||
log.error("上传目录不可写: {}", uploadSavePath);
|
||||
}
|
||||
|
||||
if (!tempDir.canWrite()) {
|
||||
log.error("临时目录不可写: {}", uploadTempPath);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("初始化上传目录失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件保存结果
|
||||
*/
|
||||
@Data
|
||||
public static class FileSaveResult {
|
||||
private String filePath; // 相对路径,用于数据库存储
|
||||
private String fileUrl; // 完整的HTTP访问URL
|
||||
private String fileName; // 保存后的文件名
|
||||
private String originalFileName; // 原始文件名
|
||||
private long fileSize; // 文件大小(字节)
|
||||
}
|
||||
}
|
||||
@@ -133,15 +133,15 @@ public class StudyCourseApi extends ApiBaseController{
|
||||
*
|
||||
* @param pager 分页参数
|
||||
* @param courseId 25.11.25新增
|
||||
*
|
||||
* @param contentName 资源名称
|
||||
*/
|
||||
@RequestMapping(value = "/pagelist-resource", method = {RequestMethod.GET, RequestMethod.POST})
|
||||
public JsonResponse<PageList<CourseFinishCountDto>> findPageResource(Pagination pager, String courseId) {
|
||||
public JsonResponse<PageList<CourseFinishCountDto>> findPageResource(Pagination pager, String courseId, String contentName) {
|
||||
if (StringUtil.isBlank(courseId)) {
|
||||
return error("课程id不能为空");
|
||||
}
|
||||
try {
|
||||
PageList<CourseFinishCountDto> courseFinishCountDTOs = service.findPageResource(pager.getPageIndex(), pager.getPageSize(), courseId);
|
||||
PageList<CourseFinishCountDto> courseFinishCountDTOs = service.findPageResource(pager.getPageIndex(), pager.getPageSize(), courseId, contentName);
|
||||
return success(courseFinishCountDTOs);
|
||||
} catch (Exception e) {
|
||||
log.error("查询课程学习记录错误:{}", e.getMessage());
|
||||
@@ -451,8 +451,8 @@ public class StudyCourseApi extends ApiBaseController{
|
||||
map.put("学习开始时间", studyCourse1.getStartTime());
|
||||
// 结束时间为空的,说明还没学习结束(有值)
|
||||
map.put("学习结束时间", studyCourse1.getFinishTime());
|
||||
// 学习时长:学习总时间是秒,现在要求的学习时间是分钟,向上取整,1秒算1分钟,60秒算1分钟,61秒算2分钟
|
||||
map.put("学习时长(分)", studyCourse1.getTotalDuration() == null ? null : (int) Math.ceil(studyCourse1.getTotalDuration() / 60.0));
|
||||
// 学习时长(保留两位小数):
|
||||
map.put("学习时长(分)", studyCourse1.getTotalDuration() == null ? null : String.format("%.2f", studyCourse1.getTotalDuration() / 60.0));
|
||||
// 学习状态需要转换
|
||||
String statusText = "";
|
||||
if (studyCourse1.getStatus() != null) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.xboe.school.study.dto.CourseFinishCountDto;
|
||||
import com.xboe.school.study.entity.StudyCourse;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -18,33 +19,41 @@ public class CourseStatDao extends BaseDao<StudyCourse> {
|
||||
* @param startIndex 分页开始索引
|
||||
* @param pageSize 分页大小
|
||||
* @param courseId 课程ID
|
||||
* @param contentName 资源名称
|
||||
* @return 课程完成人数统计DTO集合
|
||||
*/
|
||||
public List<CourseFinishCountDto> findFinishCountPage(int startIndex, int pageSize, String courseId) {
|
||||
public List<CourseFinishCountDto> findFinishCountPage(int startIndex, int pageSize, String courseId, String contentName) {
|
||||
StringBuilder sql = new StringBuilder();
|
||||
sql.append("SELECT ")
|
||||
// 课程名(和DTO字段对应)
|
||||
.append("c.content_name AS contentName, ")
|
||||
// 资源名
|
||||
.append("cc.content_name AS contentName, ")
|
||||
// 资源ID
|
||||
.append("cc.id AS contentId, ")
|
||||
// 完成人数(去重统计)
|
||||
.append("COUNT(DISTINCT c.aid) AS finishCount, ")
|
||||
//2025.11.27新增:资源类型
|
||||
.append("cc.content_type AS contentType ")
|
||||
// 你的课程表名
|
||||
.append("FROM boe_study_course_item c ")
|
||||
.append("FROM boe_course_content cc ")
|
||||
// 2025.11.27新增:连表 boe_course_content
|
||||
.append("LEFT JOIN boe_course_content cc ON cc.id = c.content_id ")
|
||||
.append("WHERE 1=1 ")
|
||||
// 条件:已完成
|
||||
.append("AND c.status = 9 ");
|
||||
.append("LEFT JOIN boe_study_course_item c ON cc.id = c.content_id AND c.status = 9 ")
|
||||
.append("WHERE 1=1 ");
|
||||
List<Object> params = new ArrayList<>();
|
||||
// courseId非空则过滤(参数化防注入)
|
||||
if (StringUtils.isNotBlank(courseId)) {
|
||||
sql.append("AND c.course_id = ? ");
|
||||
sql.append("AND cc.course_id = ? ");
|
||||
params.add(courseId);
|
||||
}
|
||||
// 25.12.1修改 新增:contentName模糊查询(粗略匹配)
|
||||
if (StringUtils.isNotBlank(contentName)) {
|
||||
// 实现“包含contentName”的模糊查询
|
||||
sql.append("AND cc.content_name LIKE ? ");
|
||||
// 通配符%拼接在参数上防注入,表示“前后任意字符包含contentName”
|
||||
params.add("%" + contentName + "%");
|
||||
}
|
||||
// 分组+排序+分页(聚合函数必须分组,排序参考第一个代码的desc id)
|
||||
sql.append("GROUP BY c.content_id, c.content_name, cc.content_type ")
|
||||
.append("ORDER BY c.content_id DESC ")
|
||||
sql.append("GROUP BY cc.id, cc.content_name, cc.content_type ")
|
||||
.append("ORDER BY cc.id DESC ")
|
||||
// MySQL分页:偏移量,每页条数
|
||||
.append("LIMIT ?, ?");
|
||||
// 补充分页参数(顺序:startIndex → pageSize)
|
||||
@@ -57,29 +66,41 @@ public class CourseStatDao extends BaseDao<StudyCourse> {
|
||||
for (Object[] objs : resultList) {
|
||||
CourseFinishCountDto dto = new CourseFinishCountDto();
|
||||
dto.setContentName(objs[0] != null ? (String) objs[0] : "");
|
||||
dto.setFinishCount(objs[1] != null ? ((Number) objs[1]).intValue() : 0);
|
||||
dto.setContentType((Integer) objs[2]);
|
||||
dto.setContentId(((BigInteger) objs[1]).toString());
|
||||
dto.setFinishCount(objs[2] != null ? ((Number) objs[2]).intValue() : 0);
|
||||
dto.setContentType((Integer) objs[3]);
|
||||
dtoList.add(dto);
|
||||
}
|
||||
return dtoList;
|
||||
}
|
||||
|
||||
// 查总条数(返回int类型,匹配PageList的count字段)
|
||||
public int findFinishCountTotal(String courseId) {
|
||||
/**
|
||||
* 查询课程完成人数
|
||||
*
|
||||
* @param courseId 课程ID
|
||||
* @param contentName 资源名称
|
||||
* @return 课程完成人数
|
||||
*/
|
||||
public int findFinishCountTotal(String courseId, String contentName) {
|
||||
StringBuilder sql = new StringBuilder();
|
||||
sql.append("SELECT ")
|
||||
// 总条数=去重后的人数
|
||||
.append("COUNT(DISTINCT c.content_id) ")
|
||||
.append("FROM boe_study_course_item c ")
|
||||
.append("WHERE 1=1 ")
|
||||
// 条件:已完成
|
||||
.append("AND c.status = 9 ");
|
||||
.append("COUNT(DISTINCT c.id) ")
|
||||
.append("FROM boe_course_content c ")
|
||||
.append("WHERE 1=1 ");
|
||||
List<Object> params = new ArrayList<>();
|
||||
// courseId非空则过滤(参数化防注入)
|
||||
if (StringUtils.isNotBlank(courseId)) {
|
||||
sql.append("AND c.course_id = ? ");
|
||||
params.add(courseId);
|
||||
}
|
||||
// 25.12.1修改 新增:contentName模糊查询(粗略匹配)
|
||||
if (StringUtils.isNotBlank(contentName)) {
|
||||
// 实现“包含contentName”的模糊查询
|
||||
sql.append("AND c.content_name LIKE ? ");
|
||||
// 通配符%拼接在参数上防注入,表示“前后任意字符包含contentName”
|
||||
params.add("%" + contentName + "%");
|
||||
}
|
||||
// 用sqlCount替代sqlFindObject,直接返回int类型结果
|
||||
// sqlCount会执行SQL并返回COUNT的结果,无需手动转换Object
|
||||
return this.sqlCount(sql.toString(), params.toArray());
|
||||
|
||||
@@ -2,6 +2,8 @@ package com.xboe.school.study.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* 课程完成人数统计DTO
|
||||
*/
|
||||
@@ -13,6 +15,11 @@ public class CourseFinishCountDto {
|
||||
*/
|
||||
private String contentName;
|
||||
|
||||
/**
|
||||
* 内容ID
|
||||
*/
|
||||
private String contentId;
|
||||
|
||||
/**
|
||||
* 完成人数(数据库 count 统计得出)
|
||||
*/
|
||||
@@ -24,4 +31,5 @@ public class CourseFinishCountDto {
|
||||
*/
|
||||
private Integer contentType;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -69,8 +69,9 @@ public interface IStudyCourseService {
|
||||
* @param pageIndex 页码
|
||||
* @param pageSize 每页数据条数
|
||||
* @param courseId 课程id
|
||||
* @param contentName 资源名称
|
||||
*/
|
||||
PageList<CourseFinishCountDto> findPageResource(int pageIndex, int pageSize, String courseId);
|
||||
PageList<CourseFinishCountDto> findPageResource(int pageIndex, int pageSize, String courseId, String contentName);
|
||||
|
||||
/**
|
||||
* 热度榜
|
||||
|
||||
@@ -2,7 +2,10 @@ package com.xboe.school.study.service.impl;
|
||||
|
||||
import com.xboe.common.OrderCondition;
|
||||
import com.xboe.common.PageList;
|
||||
import com.xboe.core.orm.*;
|
||||
import com.xboe.core.orm.FieldFilters;
|
||||
import com.xboe.core.orm.FieldUpdateType;
|
||||
import com.xboe.core.orm.QueryBuilder;
|
||||
import com.xboe.core.orm.UpdateBuilder;
|
||||
import com.xboe.core.utils.ConvertUtil;
|
||||
import com.xboe.data.outside.IOutSideDataService;
|
||||
import com.xboe.module.course.dao.CourseContentDao;
|
||||
@@ -24,15 +27,17 @@ import com.xboe.system.user.vo.UserSimpleVo;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.transaction.Transactional;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -56,6 +61,10 @@ public class StudyCourseServiceImpl implements IStudyCourseService{
|
||||
@Autowired
|
||||
IOrganizationService organizationService;
|
||||
|
||||
@Autowired
|
||||
@Lazy
|
||||
IStudyCourseService studyCourseService;
|
||||
|
||||
@Autowired
|
||||
StudyCourseDao studyCourseDao;
|
||||
|
||||
@@ -92,6 +101,13 @@ public class StudyCourseServiceImpl implements IStudyCourseService{
|
||||
@Autowired(required = false)
|
||||
ICourseStudySearch esSearch;//对ES的处理
|
||||
|
||||
/**
|
||||
* 用于避免JPA查询后修改entity实体字段,自动更新到数据库
|
||||
*/
|
||||
@Autowired
|
||||
private EntityManager entityManager;
|
||||
|
||||
|
||||
@Override
|
||||
public StudyCourse findByCourseIdAndAid(String courseId, String aid) {
|
||||
//加上排序,如果是多条学习记录,只会取最新的一条
|
||||
@@ -146,36 +162,6 @@ public class StudyCourseServiceImpl implements IStudyCourseService{
|
||||
if(StringUtils.isNotBlank(sc.getAid())) {
|
||||
query.addFilter(FieldFilters.eq("aid", sc.getAid()));
|
||||
}
|
||||
// 25.11.25新增:添加基于时间的查询条件
|
||||
if (StringUtils.isNotBlank(sc.getQueryStartTime()) && StringUtils.isNotBlank(sc.getQueryFinishTime())) {
|
||||
// 开始结束时间均传入的情况,实现筛选逻辑
|
||||
// (startTime >= 查询开始时间 AND startTime <= 查询结束时间) OR (finishTime >= 查询开始时间 AND finishTime <= 查询结束时间)
|
||||
// 这样兼容查询结束时间为空值的情况,因为学员课程未结束时没有结束时间
|
||||
LocalDate startDate = LocalDate.parse(sc.getQueryStartTime());
|
||||
LocalDateTime startDateTime = startDate.atStartOfDay();
|
||||
LocalDate finishDate = LocalDate.parse(sc.getQueryFinishTime());
|
||||
LocalDateTime finishDateTime = finishDate.atTime(LocalTime.MAX);
|
||||
// 筛选开始时间
|
||||
IFieldFilter startTimeInRange = FieldFilters.and(FieldFilters.ge("startTime", startDateTime), FieldFilters.le("startTime", finishDateTime));
|
||||
// 筛选结束时间
|
||||
IFieldFilter finishTimeInRange = FieldFilters.and(FieldFilters.ge("finishTime", startDateTime), FieldFilters.le("finishTime", finishDateTime));
|
||||
// OR条件查询
|
||||
query.addFilter(FieldFilters.or(startTimeInRange, finishTimeInRange));
|
||||
} else {
|
||||
// 只输出单个参数情况
|
||||
// 筛选开始时间
|
||||
if (StringUtils.isNotBlank(sc.getQueryStartTime())) {
|
||||
LocalDate startDate = LocalDate.parse(sc.getQueryStartTime());
|
||||
LocalDateTime startDateTime = startDate.atStartOfDay();
|
||||
query.addFilter(FieldFilters.ge("startTime", startDateTime));
|
||||
}
|
||||
// 筛选结束时间
|
||||
if (StringUtils.isNotBlank(sc.getQueryFinishTime())) {
|
||||
LocalDate finishDate = LocalDate.parse(sc.getQueryFinishTime());
|
||||
LocalDateTime finishDateTime = finishDate.atTime(LocalTime.MAX);
|
||||
query.addFilter(FieldFilters.le("finishTime", finishDateTime));
|
||||
}
|
||||
}
|
||||
}
|
||||
// 原有查询是否结束逻辑
|
||||
if (isFinish != null) {
|
||||
@@ -231,17 +217,73 @@ public class StudyCourseServiceImpl implements IStudyCourseService{
|
||||
}
|
||||
}
|
||||
}
|
||||
// 25.12.3新增,添加基于时间的查询条件,规则如下:
|
||||
if (sc != null && StringUtils.isNotBlank(sc.getCourseId()) && StringUtils.isNotBlank(sc.getQueryStartTime()) && StringUtils.isNotBlank(sc.getQueryFinishTime())) {
|
||||
List<StudyCourseItem> studyCourseItemList = studyCourseService.getStudyCourseItemByCourseId(sc.getCourseId());
|
||||
if (studyCourseItemList != null && !studyCourseItemList.isEmpty()) {
|
||||
// 将学习内容按照studyId进行分组,便于后续计算每个studyId下的总学习时长
|
||||
Map<String, List<StudyCourseItem>> itemsByStudyId = studyCourseItemList.stream().filter(item -> item.getFinishTime() != null).collect(Collectors.groupingBy(StudyCourseItem::getStudyId));
|
||||
// 获取查询的起止时间
|
||||
String queryStartTime = sc.getQueryStartTime();
|
||||
String queryFinishTime = sc.getQueryFinishTime();
|
||||
for (StudyCourse studyCourse : studyCourses) {
|
||||
// 实体类解耦
|
||||
entityManager.detach(studyCourse);
|
||||
// 解析查询时间范围
|
||||
LocalDate startDate = LocalDate.parse(queryStartTime);
|
||||
LocalDate endDate = LocalDate.parse(queryFinishTime);
|
||||
// 如果有查询时间范围,则计算筛选后的学习时长
|
||||
if (itemsByStudyId.containsKey(studyCourse.getId())) {
|
||||
List<StudyCourseItem> items = itemsByStudyId.get(studyCourse.getId());
|
||||
int filteredDuration = 0;
|
||||
for (StudyCourseItem item : items) {
|
||||
LocalDateTime finishTime = item.getFinishTime();
|
||||
LocalDate finishDate = finishTime.toLocalDate();
|
||||
// 判断结束时间是否在筛选范围内
|
||||
boolean inRange = true;
|
||||
if (finishDate.isBefore(startDate)) {
|
||||
inRange = false;
|
||||
}
|
||||
if (finishDate.isAfter(endDate)) {
|
||||
inRange = false;
|
||||
}
|
||||
// 如果在范围内,则累加学习时长
|
||||
if (inRange) {
|
||||
Integer duration = item.getStudyDuration();
|
||||
if (duration != null) {
|
||||
filteredDuration += duration;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 更新学习时长
|
||||
studyCourse.setTotalDuration(filteredDuration);
|
||||
} else {
|
||||
// 查询不到item的情况,默认置空
|
||||
studyCourse.setTotalDuration(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return studyCoursePageList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询课程的资源名称以及资源学习完成人数
|
||||
*
|
||||
* @param pageIndex 页码
|
||||
* @param pageSize 每页数据条数
|
||||
* @param courseId 课程id
|
||||
* @param contentName 资源名称
|
||||
*/
|
||||
@Override
|
||||
public PageList<CourseFinishCountDto> findPageResource(int pageIndex, int pageSize, String courseId) {
|
||||
public PageList<CourseFinishCountDto> findPageResource(int pageIndex, int pageSize, String courseId, String contentName) {
|
||||
// 1. 手动计算分页偏移量(数据库分页必需)
|
||||
// pageIndex<1时设为0,避免数据库LIMIT偏移量为负数
|
||||
int startIndex = (pageIndex < 1) ? 0 : (pageIndex - 1) * pageSize;
|
||||
// 2. 调用Dao层:查当前页数据(传入偏移量、每页条数、courseId条件)
|
||||
List<CourseFinishCountDto> dtoList = courseStatDao.findFinishCountPage(startIndex, pageSize, courseId);
|
||||
List<CourseFinishCountDto> dtoList = courseStatDao.findFinishCountPage(startIndex, pageSize, courseId,contentName);
|
||||
// 3. 调用Dao层:查总条数(对应PageList的count字段,用int类型,和PageList一致)
|
||||
int totalCount = courseStatDao.findFinishCountTotal(courseId);
|
||||
int totalCount = courseStatDao.findFinishCountTotal(courseId,contentName);
|
||||
// 4. 按PageList构造函数创建对象(只传list和count)
|
||||
PageList<CourseFinishCountDto> pageList = new PageList<>(dtoList, totalCount);
|
||||
// 5. 设置pageSize(覆盖默认10,确保总页数计算正确)
|
||||
@@ -809,36 +851,6 @@ public class StudyCourseServiceImpl implements IStudyCourseService{
|
||||
if (StringUtils.isNotBlank(sc.getAid())) {
|
||||
query.addFilter(FieldFilters.eq("aid", sc.getAid()));
|
||||
}
|
||||
// 25.11.25新增:添加基于时间的查询条件
|
||||
if (sc.getQueryStartTime() != null && sc.getQueryFinishTime() != null) {
|
||||
// 开始结束时间均传入的情况,实现筛选逻辑
|
||||
// (startTime >= 查询开始时间 AND startTime <= 查询结束时间) OR (finishTime >= 查询开始时间 AND finishTime <= 查询结束时间)
|
||||
// 这样兼容查询结束时间为空值的情况,因为学员课程未结束时没有结束时间
|
||||
LocalDate startDate = LocalDate.parse(sc.getQueryStartTime());
|
||||
LocalDateTime startDateTime = startDate.atStartOfDay();
|
||||
LocalDate finishDate = LocalDate.parse(sc.getQueryFinishTime());
|
||||
LocalDateTime finishDateTime = finishDate.atTime(LocalTime.MAX);
|
||||
// 筛选开始时间
|
||||
IFieldFilter startTimeInRange = FieldFilters.and(FieldFilters.ge("startTime", startDateTime), FieldFilters.le("startTime", finishDateTime));
|
||||
// 筛选结束时间
|
||||
IFieldFilter finishTimeInRange = FieldFilters.and(FieldFilters.ge("finishTime", startDateTime), FieldFilters.le("finishTime", finishDateTime));
|
||||
// OR条件查询
|
||||
query.addFilter(FieldFilters.or(startTimeInRange, finishTimeInRange));
|
||||
} else {
|
||||
// 只输出单个参数情况
|
||||
// 筛选开始时间
|
||||
if (sc.getQueryStartTime() != null) {
|
||||
LocalDate startDate = LocalDate.parse(sc.getQueryStartTime());
|
||||
LocalDateTime startDateTime = startDate.atStartOfDay();
|
||||
query.addFilter(FieldFilters.ge("startTime", startDateTime));
|
||||
}
|
||||
// 筛选结束时间
|
||||
if (sc.getQueryFinishTime() != null) {
|
||||
LocalDate finishDate = LocalDate.parse(sc.getQueryFinishTime());
|
||||
LocalDateTime finishDateTime = finishDate.atTime(LocalTime.MAX);
|
||||
query.addFilter(FieldFilters.le("finishTime", finishDateTime));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isFinish != null) {
|
||||
if (isFinish) {
|
||||
@@ -847,7 +859,55 @@ public class StudyCourseServiceImpl implements IStudyCourseService{
|
||||
query.addFilter(FieldFilters.lt("status", 9));
|
||||
}
|
||||
}
|
||||
return studyCourseDao.findList(query.builder());
|
||||
// 25.12.3新增,添加基于时间的查询条件,规则如下:
|
||||
List<StudyCourse> studyCourses = studyCourseDao.findList(query.builder());
|
||||
if (sc != null && StringUtils.isNotBlank(sc.getCourseId()) && StringUtils.isNotBlank(sc.getQueryStartTime()) && StringUtils.isNotBlank(sc.getQueryFinishTime())) {
|
||||
List<StudyCourseItem> studyCourseItemList = studyCourseService.getStudyCourseItemByCourseId(sc.getCourseId());
|
||||
if (studyCourseItemList != null && !studyCourseItemList.isEmpty()) {
|
||||
// 将学习内容按照studyId进行分组,便于后续计算每个studyId下的总学习时长
|
||||
Map<String, List<StudyCourseItem>> itemsByStudyId = studyCourseItemList.stream().filter(item -> item.getFinishTime() != null).collect(Collectors.groupingBy(StudyCourseItem::getStudyId));
|
||||
// 获取查询的起止时间
|
||||
String queryStartTime = sc.getQueryStartTime();
|
||||
String queryFinishTime = sc.getQueryFinishTime();
|
||||
for (StudyCourse studyCourse : studyCourses) {
|
||||
// 实体类解耦
|
||||
entityManager.detach(studyCourse);
|
||||
// 解析查询时间范围
|
||||
LocalDate startDate = LocalDate.parse(queryStartTime);
|
||||
LocalDate endDate = LocalDate.parse(queryFinishTime);
|
||||
// 如果有查询时间范围,则计算筛选后的学习时长
|
||||
if (itemsByStudyId.containsKey(studyCourse.getId())) {
|
||||
List<StudyCourseItem> items = itemsByStudyId.get(studyCourse.getId());
|
||||
int filteredDuration = 0;
|
||||
for (StudyCourseItem item : items) {
|
||||
LocalDateTime finishTime = item.getFinishTime();
|
||||
LocalDate finishDate = finishTime.toLocalDate();
|
||||
// 判断结束时间是否在筛选范围内
|
||||
boolean inRange = true;
|
||||
if (finishDate.isBefore(startDate)) {
|
||||
inRange = false;
|
||||
}
|
||||
if (finishDate.isAfter(endDate)) {
|
||||
inRange = false;
|
||||
}
|
||||
// 如果在范围内,则累加学习时长
|
||||
if (inRange) {
|
||||
Integer duration = item.getStudyDuration();
|
||||
if (duration != null) {
|
||||
filteredDuration += duration;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 更新学习时长
|
||||
studyCourse.setTotalDuration(filteredDuration);
|
||||
} else {
|
||||
// 查询不到item的情况,默认置空
|
||||
studyCourse.setTotalDuration(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return studyCourses;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -251,7 +251,7 @@ public class StudyServiceImpl implements IStudyService{
|
||||
query.addFilter(FieldFilters.eq("courseId",courseId));
|
||||
}
|
||||
if(StringUtils.isNotBlank(name)) {
|
||||
query.addFilter(FieldFilters.eq("aname", name));
|
||||
query.addFilter(FieldFilters.like("aname", name));
|
||||
}
|
||||
int pageIndex2 = (pageIndex-1)*10;
|
||||
if(status!=null) {
|
||||
@@ -272,7 +272,7 @@ public class StudyServiceImpl implements IStudyService{
|
||||
}
|
||||
}
|
||||
// 未传输status的情况,查询所有资源学习情况数据
|
||||
String sql = "select a.id, a.course_id, a.course_name, a.aname, " + "IFNULL(b.finish_time, '0') as finish_time, IFNULL(b.progress, 0) as progress, IFNULL(b.status, 1) as status " + ",b.score,b.item_id " + "from (select id, course_id, course_name, aname, 0, 1 from boe_study_course where course_id = '" + courseId + "'" + (StringUtils.isBlank(name) ? "" : "and aname like '%" + name + "%'") + ") a " + "inner join " + "(select bsc.id, bsc.course_id, bsc.course_name, bsc.aname, item.id as item_id,item.finish_time, item.progress, item.status,MAX(item.score) score " + "from boe_study_course bsc left join boe_study_course_item item on item.course_id = bsc.course_id and item.study_id = bsc.id " + "where bsc.course_id = '" + courseId + "'" +
|
||||
String sql = "select a.id, a.course_id, a.course_name, a.aname, " + "IFNULL(b.finish_time, '0') as finish_time, IFNULL(b.progress, 0) as progress, IFNULL(b.status, 1) as status " + ",b.score,b.item_id,b.aid " + "from (select id, course_id, course_name, aname, 0, 1 from boe_study_course where course_id = '" + courseId + "'" + (StringUtils.isBlank(name) ? "" : "and aname like '%" + name + "%'") + ") a " + "inner join " + "(select bsc.id, bsc.course_id, bsc.course_name, bsc.aname, item.id as item_id,item.finish_time, item.progress, item.status,MAX(item.score) score,item.aid " + "from boe_study_course bsc left join boe_study_course_item item on item.course_id = bsc.course_id and item.study_id = bsc.id " + "where bsc.course_id = '" + courseId + "'" +
|
||||
(StringUtils.isBlank(contentId) ? "" : "and item.content_id = '" + contentId + "'") +
|
||||
(StringUtils.isBlank(name) ? "" : "and item.aname like '%" + name +"%'") + " group by bsc.id) b " +
|
||||
"on a.course_id = b.course_id and a.id = b.id " +
|
||||
@@ -284,6 +284,7 @@ public class StudyServiceImpl implements IStudyService{
|
||||
"on a.course_id = b.course_id and a.id = b.id " +
|
||||
"group by a.id) as total";
|
||||
log.info("资源完成情况sql:{}", sql);
|
||||
log.info("数量查询sql:{}", sql2);
|
||||
List<Object[]> list = scDao.sqlFindList(sql);
|
||||
int totalCount = scDao.sqlCount(sql2);
|
||||
List<StudyCourseItem> item = new ArrayList<>();
|
||||
@@ -304,6 +305,10 @@ public class StudyServiceImpl implements IStudyService{
|
||||
if(objs[7] != null){
|
||||
sc.setScore(Float.valueOf(objs[7].toString()));
|
||||
}
|
||||
// 25.12.5新增,补全aid查询
|
||||
if (objs[8] != null) {
|
||||
sc.setAid(String.valueOf(objs[8].toString()));
|
||||
}
|
||||
item.add(sc);
|
||||
}
|
||||
PageList<StudyCourseItem> pageList = new PageList<>(item);
|
||||
|
||||
@@ -16,12 +16,15 @@ spring:
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: 192.168.0.253:8848
|
||||
server-addr: 127.0.0.1:8848
|
||||
# server-addr: 192.168.0.253:8848
|
||||
config:
|
||||
server-addr: 192.168.0.253:8848
|
||||
server-addr: 127.0.0.1:8848
|
||||
# server-addr: 192.168.0.253:8848
|
||||
redis:
|
||||
database: 1
|
||||
host: 192.168.0.253
|
||||
host: 127.0.0.1
|
||||
# host: 192.168.0.253
|
||||
password: boe@123
|
||||
port: 6379
|
||||
jpa:
|
||||
@@ -29,9 +32,9 @@ spring:
|
||||
ddl-auto: none
|
||||
datasource:
|
||||
driverClassName: com.mysql.jdbc.Driver
|
||||
url: jdbc:mysql://192.168.0.253:3306/boe_base?useSSL=false&useUnicode=true&characterEncoding=UTF8&zeroDateTimeBehavior=convertToNull
|
||||
username: root
|
||||
password: boe#1234A
|
||||
url: jdbc:mysql://rm-hp3cpkk0u50q90eu9vo.mysql.huhehaote.rds.aliyuncs.com:3306/ebiz_doc_manage_bpic?characterEncoding=utf8&useUnicode=true&serverTimezone=Asia/Shanghai&useSSL=false&allowMultiQueries=true
|
||||
username: ebiz_ai
|
||||
password: ebiz_ai123
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
hikari:
|
||||
auto-commit: true
|
||||
@@ -96,14 +99,15 @@ xboe:
|
||||
- liu.zixi@ebiz-digits.com
|
||||
xxl:
|
||||
job:
|
||||
accessToken: 65ddc683-22f5-83b4-de3a-3c97a0a29af0
|
||||
# accessToken: 65ddc683-22f5-83b4-de3a-3c97a0a29af0
|
||||
accessToken: default_token
|
||||
admin:
|
||||
addresses: http://192.168.0.253/jobAdmin
|
||||
addresses: http://127.0.0.1:8080/xxl-job-admin
|
||||
executor:
|
||||
appname: java-servers-job-api
|
||||
port: 9995
|
||||
address:
|
||||
ip:
|
||||
ip: 127.0.0.1
|
||||
logpath: /var/log/xxl-job/dw/
|
||||
logretentiondays: 30
|
||||
aop-log-record:
|
||||
|
||||
@@ -146,6 +146,7 @@ boe:
|
||||
domain-name: https://u.boe.com
|
||||
pcPageUrl: ${boe.domain-name}/pc/course/studyindex?id=
|
||||
h5PageUrl: ${boe.domain-name}/mobile/pages/study/courseStudy?id=
|
||||
# 问题所在:访问不了------- 页面闪退+502+404
|
||||
pcLoginUrl: ${boe.domain-name}/web/
|
||||
h5LoginUrl: ${boe.domain-name}/m/loginuser
|
||||
|
||||
|
||||
Reference in New Issue
Block a user