| | |
| | | import com.tcm.training.admin.live.entity.HealthCourseVideo; |
| | | import com.tcm.training.admin.live.entity.LiveCourseCategory; |
| | | import com.tcm.training.admin.live.excel.HealthCourseExportRow; |
| | | import com.tcm.training.admin.live.excel.HealthCourseVideoImportRow; |
| | | import com.tcm.training.admin.live.mapper.ExpertMapper; |
| | | import com.tcm.training.admin.live.mapper.HealthCourseMapper; |
| | | import com.tcm.training.admin.live.mapper.HealthCourseVideoMapper; |
| | |
| | | import com.tcm.training.admin.live.vo.HealthCourseDetailVO; |
| | | import com.tcm.training.admin.live.vo.HealthCourseListItemVO; |
| | | import com.tcm.training.admin.live.vo.HealthCourseListRow; |
| | | import com.tcm.training.admin.live.vo.HealthCourseOptionVO; |
| | | import com.tcm.training.admin.live.vo.HealthCourseStatusVO; |
| | | import com.tcm.training.admin.live.vo.HealthCourseVideoDetailVO; |
| | | import com.tcm.training.admin.live.vo.HealthCourseVideoItemVO; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | |
| | | import javax.servlet.http.HttpServletResponse; |
| | | import java.io.IOException; |
| | |
| | | import java.net.URLEncoder; |
| | | import java.time.LocalDateTime; |
| | | import java.util.ArrayList; |
| | | import java.util.Collection; |
| | | import java.util.Collections; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | |
| | | @Service |
| | | public class HealthCourseAppService { |
| | |
| | | Integer status) throws IOException { |
| | | response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); |
| | | response.setCharacterEncoding("utf-8"); |
| | | String filename = URLEncoder.encode("养身课程", "UTF-8").replace("+", "%20"); |
| | | String filename = URLEncoder.encode("养生课程", "UTF-8").replace("+", "%20"); |
| | | response.setHeader("Content-Disposition", "attachment;filename*=utf-8''" + filename + ".xlsx"); |
| | | |
| | | List<HealthCourseListRow> rows = healthCourseMapper.selectCourseList( |
| | |
| | | exports.add(export); |
| | | } |
| | | FastExcel.write(response.getOutputStream(), HealthCourseExportRow.class) |
| | | .sheet("养身课程") |
| | | .sheet("养生课程") |
| | | .doWrite(exports); |
| | | } |
| | | |
| | | public List<HealthCourseOptionVO> options(String keyword) { |
| | | List<HealthCourse> courses = healthCourseMapper.selectList(new LambdaQueryWrapper<HealthCourse>() |
| | | .eq(HealthCourse::getDeleted, 0) |
| | | .like(keyword != null && !keyword.trim().isEmpty(), HealthCourse::getTitle, keyword == null ? null : keyword.trim()) |
| | | .orderByAsc(HealthCourse::getSort) |
| | | .orderByDesc(HealthCourse::getId)); |
| | | List<HealthCourseOptionVO> list = new ArrayList<HealthCourseOptionVO>(); |
| | | for (HealthCourse course : courses) { |
| | | list.add(new HealthCourseOptionVO(course.getId(), course.getTitle())); |
| | | } |
| | | return list; |
| | | } |
| | | |
| | | public PageResponse<HealthCourseVideoItemVO> pageVideos(Long courseId, |
| | | int page, |
| | | int pageSize, |
| | | String chapterName, |
| | | Integer saleType) { |
| | | Integer priceType) { |
| | | requireCourse(courseId); |
| | | LambdaQueryWrapper<HealthCourseVideo> qw = new LambdaQueryWrapper<HealthCourseVideo>() |
| | | .eq(HealthCourseVideo::getCourseId, courseId) |
| | |
| | | if (chapterName != null && !chapterName.trim().isEmpty()) { |
| | | qw.like(HealthCourseVideo::getChapterName, chapterName.trim()); |
| | | } |
| | | if (saleType != null) { |
| | | qw.eq(HealthCourseVideo::getSaleType, saleType); |
| | | if (priceType != null) { |
| | | if (priceType == 0) { |
| | | qw.eq(HealthCourseVideo::getSalePrice, BigDecimal.ZERO); |
| | | } else if (priceType == 1) { |
| | | qw.gt(HealthCourseVideo::getSalePrice, BigDecimal.ZERO); |
| | | } |
| | | } |
| | | IPage<HealthCourseVideo> result = healthCourseVideoMapper.selectPage(new Page<HealthCourseVideo>(page, pageSize), qw); |
| | | List<HealthCourseVideoItemVO> list = new ArrayList<HealthCourseVideoItemVO>(); |
| | | for (HealthCourseVideo item : result.getRecords()) { |
| | | list.add(toVideoItem(item)); |
| | | } |
| | | return PageResponse.of(list, result.getTotal(), result.getCurrent(), result.getSize()); |
| | | } |
| | | |
| | | public PageResponse<HealthCourseVideoItemVO> pageVideoResources(int page, |
| | | int pageSize, |
| | | String courseKeyword, |
| | | Long courseId, |
| | | Integer courseType, |
| | | String chapterName, |
| | | Integer priceType, |
| | | Integer status) { |
| | | List<Long> matchedCourseIds = resolveMatchedCourseIds(courseKeyword, courseId); |
| | | if (matchedCourseIds.isEmpty()) { |
| | | return PageResponse.of(Collections.<HealthCourseVideoItemVO>emptyList(), 0, page, pageSize); |
| | | } |
| | | LambdaQueryWrapper<HealthCourseVideo> qw = new LambdaQueryWrapper<HealthCourseVideo>() |
| | | .in(HealthCourseVideo::getCourseId, matchedCourseIds) |
| | | .eq(HealthCourseVideo::getDeleted, 0) |
| | | .eq(courseType != null, HealthCourseVideo::getCourseType, courseType) |
| | | .eq(status != null, HealthCourseVideo::getStatus, status) |
| | | .orderByAsc(HealthCourseVideo::getSort) |
| | | .orderByDesc(HealthCourseVideo::getId); |
| | | if (chapterName != null && !chapterName.trim().isEmpty()) { |
| | | qw.like(HealthCourseVideo::getChapterName, chapterName.trim()); |
| | | } |
| | | if (priceType != null) { |
| | | if (priceType == 0) { |
| | | qw.eq(HealthCourseVideo::getSalePrice, BigDecimal.ZERO); |
| | | } else if (priceType == 1) { |
| | | qw.gt(HealthCourseVideo::getSalePrice, BigDecimal.ZERO); |
| | | } |
| | | } |
| | | IPage<HealthCourseVideo> result = healthCourseVideoMapper.selectPage(new Page<HealthCourseVideo>(page, pageSize), qw); |
| | | Map<Long, String> courseTitleMap = resolveCourseTitleMap(extractCourseIds(result.getRecords())); |
| | | List<HealthCourseVideoItemVO> list = new ArrayList<HealthCourseVideoItemVO>(); |
| | | for (HealthCourseVideo item : result.getRecords()) { |
| | | list.add(toVideoItem(item, courseTitleMap.get(item.getCourseId()))); |
| | | } |
| | | return PageResponse.of(list, result.getTotal(), result.getCurrent(), result.getSize()); |
| | | } |
| | |
| | | |
| | | @Transactional |
| | | public HealthCourseVideoDetailVO createVideo(Long courseId, HealthCourseVideoSaveRequest req) { |
| | | requireCourse(courseId); |
| | | validateVideoSale(req); |
| | | HealthCourseVideo next = new HealthCourseVideo(); |
| | | next.setCourseId(courseId); |
| | | fillVideo(next, req); |
| | | next.setSort(resolveNextVideoSort(courseId)); |
| | | next.setDeleted(0); |
| | | healthCourseVideoMapper.insert(next); |
| | | refreshCourseStats(courseId); |
| | | return detailVideo(courseId, next.getId()); |
| | | req.setCourseId(courseId); |
| | | return createVideoResource(req); |
| | | } |
| | | |
| | | @Transactional |
| | | public HealthCourseVideoDetailVO updateVideo(Long courseId, Long videoId, HealthCourseVideoSaveRequest req) { |
| | | validateVideoSale(req); |
| | | HealthCourseVideo video = requireVideo(courseId, videoId); |
| | | fillVideo(video, req); |
| | | healthCourseVideoMapper.updateById(video); |
| | | refreshCourseStats(courseId); |
| | | return detailVideo(courseId, videoId); |
| | | req.setCourseId(courseId); |
| | | return updateVideoResource(videoId, req); |
| | | } |
| | | |
| | | @Transactional |
| | |
| | | requireVideo(courseId, videoId); |
| | | healthCourseVideoMapper.deleteById(videoId); |
| | | refreshCourseStats(courseId); |
| | | } |
| | | |
| | | public HealthCourseVideoDetailVO detailVideoResource(Long id) { |
| | | HealthCourseVideo video = requireVideoById(id); |
| | | return toVideoDetail(video, requireCourse(video.getCourseId()).getTitle()); |
| | | } |
| | | |
| | | @Transactional |
| | | public HealthCourseVideoDetailVO createVideoResource(HealthCourseVideoSaveRequest req) { |
| | | Long courseId = requireVideoCourseId(req.getCourseId()); |
| | | validateVideo(req); |
| | | HealthCourseVideo next = new HealthCourseVideo(); |
| | | next.setCourseId(courseId); |
| | | fillVideo(next, req); |
| | | next.setSort(resolveNextVideoSort(courseId)); |
| | | next.setDeleted(0); |
| | | healthCourseVideoMapper.insert(next); |
| | | refreshCourseStats(courseId); |
| | | return detailVideoResource(next.getId()); |
| | | } |
| | | |
| | | @Transactional |
| | | public HealthCourseVideoDetailVO updateVideoResource(Long id, HealthCourseVideoSaveRequest req) { |
| | | HealthCourseVideo video = requireVideoById(id); |
| | | Long targetCourseId = req.getCourseId() == null ? video.getCourseId() : requireVideoCourseId(req.getCourseId()); |
| | | req.setCourseId(targetCourseId); |
| | | validateVideo(req); |
| | | Long oldCourseId = video.getCourseId(); |
| | | video.setCourseId(targetCourseId); |
| | | fillVideo(video, req); |
| | | healthCourseVideoMapper.updateById(video); |
| | | refreshCourseStats(targetCourseId); |
| | | if (!oldCourseId.equals(targetCourseId)) { |
| | | refreshCourseStats(oldCourseId); |
| | | } |
| | | return detailVideoResource(id); |
| | | } |
| | | |
| | | @Transactional |
| | | public void deleteVideoResource(Long id) { |
| | | HealthCourseVideo video = requireVideoById(id); |
| | | healthCourseVideoMapper.deleteById(id); |
| | | refreshCourseStats(video.getCourseId()); |
| | | } |
| | | |
| | | public void downloadVideoResourceTemplate(HttpServletResponse response) throws IOException { |
| | | response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); |
| | | response.setCharacterEncoding("utf-8"); |
| | | String filename = URLEncoder.encode("视频与课程资源导入模板", "UTF-8").replace("+", "%20"); |
| | | response.setHeader("Content-Disposition", "attachment;filename*=utf-8''" + filename + ".xlsx"); |
| | | List<HealthCourseVideoImportRow> rows = new ArrayList<HealthCourseVideoImportRow>(); |
| | | HealthCourseVideoImportRow sample = new HealthCourseVideoImportRow(); |
| | | sample.setCourseId(1L); |
| | | sample.setCourseTitle("中医针灸资格证"); |
| | | sample.setCourseTypeText(courseTypeText(HealthCourseVideo.COURSE_TYPE_HEALTH)); |
| | | sample.setChapterName("方剂学"); |
| | | sample.setSalePrice(BigDecimal.ZERO); |
| | | sample.setDurationSeconds(238); |
| | | sample.setVideoUrl("https://example.com/video.mp4"); |
| | | sample.setStatusText(statusText(1)); |
| | | rows.add(sample); |
| | | FastExcel.write(response.getOutputStream(), HealthCourseVideoImportRow.class) |
| | | .sheet("视频资源导入模板") |
| | | .doWrite(rows); |
| | | } |
| | | |
| | | @Transactional |
| | | public void importVideoResources(MultipartFile file) throws IOException { |
| | | if (file == null || file.isEmpty()) { |
| | | throw new IllegalArgumentException("请选择导入文件"); |
| | | } |
| | | List<HealthCourseVideoImportRow> rows = FastExcel.read(file.getInputStream()) |
| | | .head(HealthCourseVideoImportRow.class) |
| | | .sheet() |
| | | .doReadSync(); |
| | | if (rows == null || rows.isEmpty()) { |
| | | throw new IllegalArgumentException("导入文件不能为空"); |
| | | } |
| | | Set<Long> touchedCourseIds = new LinkedHashSet<Long>(); |
| | | int rowNum = 1; |
| | | for (HealthCourseVideoImportRow row : rows) { |
| | | rowNum++; |
| | | if (row == null || isImportRowEmpty(row)) { |
| | | continue; |
| | | } |
| | | validateImportRow(row, rowNum); |
| | | HealthCourse course = requireCourse(row.getCourseId()); |
| | | HealthCourseVideo video = new HealthCourseVideo(); |
| | | video.setCourseId(course.getId()); |
| | | video.setCourseType(HealthCourseVideo.COURSE_TYPE_HEALTH); |
| | | video.setChapterName(row.getChapterName().trim()); |
| | | video.setVideoSourceType(HealthCourseVideo.VIDEO_SOURCE_URL); |
| | | video.setVideoUrl(row.getVideoUrl().trim()); |
| | | video.setVideoFileName(null); |
| | | video.setDurationSeconds(row.getDurationSeconds()); |
| | | video.setSalePrice(row.getSalePrice() == null ? BigDecimal.ZERO : row.getSalePrice()); |
| | | video.setSort(resolveNextVideoSort(course.getId())); |
| | | video.setStatus(parseStatusText(row.getStatusText())); |
| | | video.setDeleted(0); |
| | | healthCourseVideoMapper.insert(video); |
| | | touchedCourseIds.add(course.getId()); |
| | | } |
| | | if (touchedCourseIds.isEmpty()) { |
| | | throw new IllegalArgumentException("导入文件没有可用数据"); |
| | | } |
| | | for (Long courseId : touchedCourseIds) { |
| | | refreshCourseStats(courseId); |
| | | } |
| | | } |
| | | |
| | | private void fillCourse(HealthCourse course, HealthCourseSaveRequest req) { |
| | |
| | | } |
| | | |
| | | private void fillVideo(HealthCourseVideo video, HealthCourseVideoSaveRequest req) { |
| | | video.setCourseType(resolveCourseType(req.getCourseType())); |
| | | video.setChapterName(req.getChapterName().trim()); |
| | | video.setChapterRemark(trimToNull(req.getChapterRemark())); |
| | | video.setVideoSourceType(req.getVideoSourceType()); |
| | | video.setVideoUrl(req.getVideoUrl().trim()); |
| | | video.setVideoFileName(trimToNull(req.getVideoFileName())); |
| | | video.setDurationSeconds(req.getDurationSeconds()); |
| | | video.setSaleType(req.getSaleType()); |
| | | video.setSalePrice(req.getSaleType() != null && req.getSaleType() == 0 ? BigDecimal.ZERO : req.getSalePrice()); |
| | | video.setPlayCount(req.getPlayCount()); |
| | | video.setStatus(req.getStatus()); |
| | | video.setSalePrice(req.getSalePrice()); |
| | | video.setStatus(resolveVideoStatus(req.getStatus())); |
| | | } |
| | | |
| | | private void validateCategories(Long levelCategoryId, Long difficultyCategoryId) { |
| | | LiveCourseCategory level = requireCategory(levelCategoryId, LiveCourseCategoryAppService.TYPE_LEVEL, "养身等级"); |
| | | LiveCourseCategory level = requireCategory(levelCategoryId, LiveCourseCategoryAppService.TYPE_LEVEL, "养生等级"); |
| | | LiveCourseCategory difficulty = requireCategory(difficultyCategoryId, LiveCourseCategoryAppService.TYPE_DIFFICULTY, "难度"); |
| | | if (level == null || difficulty == null) { |
| | | throw new IllegalArgumentException("分类不存在"); |
| | |
| | | } |
| | | } |
| | | |
| | | private void validateVideoSale(HealthCourseVideoSaveRequest req) { |
| | | if (req.getSaleType() == null || (req.getSaleType() != 0 && req.getSaleType() != 1)) { |
| | | throw new IllegalArgumentException("售价类型不合法"); |
| | | private void validateVideo(HealthCourseVideoSaveRequest req) { |
| | | Integer courseType = resolveCourseType(req.getCourseType()); |
| | | if (courseType != HealthCourseVideo.COURSE_TYPE_HEALTH) { |
| | | throw new IllegalArgumentException("课程类型不合法"); |
| | | } |
| | | if (req.getSaleType() == 0 && req.getSalePrice() != null && req.getSalePrice().compareTo(BigDecimal.ZERO) != 0) { |
| | | throw new IllegalArgumentException("免费课程售价必须为0"); |
| | | if (req.getCourseId() == null) { |
| | | throw new IllegalArgumentException("请选择关联课程"); |
| | | } |
| | | if (req.getVideoSourceType() == null |
| | | || (req.getVideoSourceType() != HealthCourseVideo.VIDEO_SOURCE_URL |
| | | && req.getVideoSourceType() != HealthCourseVideo.VIDEO_SOURCE_UPLOAD)) { |
| | | throw new IllegalArgumentException("视频来源不合法"); |
| | | } |
| | | if (req.getVideoUrl() == null || req.getVideoUrl().trim().isEmpty()) { |
| | | throw new IllegalArgumentException("请输入视频地址"); |
| | | } |
| | | if (req.getSalePrice() != null && req.getSalePrice().compareTo(BigDecimal.ZERO) < 0) { |
| | | throw new IllegalArgumentException("售价不能小于0"); |
| | | } |
| | | } |
| | | |
| | | private Integer resolveVideoStatus(Integer status) { |
| | | return status == null ? 1 : status; |
| | | } |
| | | |
| | | private int resolveNextVideoSort(Long courseId) { |
| | |
| | | |
| | | private HealthCourseVideo requireVideo(Long courseId, Long videoId) { |
| | | requireCourse(courseId); |
| | | HealthCourseVideo video = healthCourseVideoMapper.selectById(videoId); |
| | | if (video == null) { |
| | | throw new IllegalArgumentException("视频课程不存在"); |
| | | } |
| | | HealthCourseVideo video = requireVideoById(videoId); |
| | | if (!courseId.equals(video.getCourseId())) { |
| | | throw new IllegalArgumentException("视频课程不属于当前课程"); |
| | | } |
| | |
| | | } |
| | | |
| | | private HealthCourseVideoItemVO toVideoItem(HealthCourseVideo item) { |
| | | return toVideoItem(item, null); |
| | | } |
| | | |
| | | private HealthCourseVideoItemVO toVideoItem(HealthCourseVideo item, String courseTitle) { |
| | | HealthCourseVideoItemVO vo = new HealthCourseVideoItemVO(); |
| | | vo.setId(item.getId()); |
| | | vo.setCourseType(resolveCourseType(item.getCourseType())); |
| | | vo.setCourseTypeText(courseTypeText(item.getCourseType())); |
| | | vo.setCourseId(item.getCourseId()); |
| | | vo.setCourseTitle(defaultText(courseTitle)); |
| | | vo.setChapterName(item.getChapterName()); |
| | | vo.setChapterRemark(item.getChapterRemark()); |
| | | vo.setVideoSourceType(item.getVideoSourceType()); |
| | | vo.setVideoUrl(item.getVideoUrl()); |
| | | vo.setVideoFileName(item.getVideoFileName()); |
| | | vo.setDurationSeconds(item.getDurationSeconds()); |
| | | vo.setSaleType(item.getSaleType()); |
| | | vo.setSalePrice(item.getSalePrice()); |
| | | vo.setPlayCount(item.getPlayCount()); |
| | | vo.setDurationText(formatDuration(defaultNumber(item.getDurationSeconds()))); |
| | | vo.setSalePrice(item.getSalePrice() == null ? BigDecimal.ZERO : item.getSalePrice()); |
| | | vo.setWatchCount(0); |
| | | vo.setSort(item.getSort()); |
| | | vo.setStatus(item.getStatus()); |
| | | vo.setStatusText(statusText(item.getStatus())); |
| | | vo.setCreatedAt(item.getCreatedAt()); |
| | | return vo; |
| | | } |
| | | |
| | | private HealthCourseVideoDetailVO toVideoDetail(HealthCourseVideo item) { |
| | | return toVideoDetail(item, null); |
| | | } |
| | | |
| | | private HealthCourseVideoDetailVO toVideoDetail(HealthCourseVideo item, String courseTitle) { |
| | | HealthCourseVideoDetailVO vo = new HealthCourseVideoDetailVO(); |
| | | vo.setId(item.getId()); |
| | | vo.setCourseType(resolveCourseType(item.getCourseType())); |
| | | vo.setCourseTypeText(courseTypeText(item.getCourseType())); |
| | | vo.setCourseId(item.getCourseId()); |
| | | vo.setCourseTitle(defaultText(courseTitle)); |
| | | vo.setChapterName(item.getChapterName()); |
| | | vo.setChapterRemark(item.getChapterRemark()); |
| | | vo.setVideoSourceType(item.getVideoSourceType()); |
| | | vo.setVideoUrl(item.getVideoUrl()); |
| | | vo.setVideoFileName(item.getVideoFileName()); |
| | | vo.setDurationSeconds(item.getDurationSeconds()); |
| | | vo.setSaleType(item.getSaleType()); |
| | | vo.setSalePrice(item.getSalePrice()); |
| | | vo.setPlayCount(item.getPlayCount()); |
| | | vo.setSalePrice(item.getSalePrice() == null ? BigDecimal.ZERO : item.getSalePrice()); |
| | | vo.setWatchCount(0); |
| | | vo.setSort(item.getSort()); |
| | | vo.setStatus(item.getStatus()); |
| | | vo.setCreatedAt(item.getCreatedAt()); |
| | |
| | | return status != null && status == 1 ? "启用" : "禁用"; |
| | | } |
| | | |
| | | private String courseTypeText(Integer courseType) { |
| | | return resolveCourseType(courseType) == HealthCourseVideo.COURSE_TYPE_HEALTH ? "养生课程" : "-"; |
| | | } |
| | | |
| | | private String defaultText(String text) { |
| | | return text == null || text.trim().isEmpty() ? "-" : text; |
| | | } |
| | |
| | | String trimmed = text.trim(); |
| | | return trimmed.isEmpty() ? null : trimmed; |
| | | } |
| | | |
| | | private Integer resolveCourseType(Integer courseType) { |
| | | return courseType == null ? HealthCourseVideo.COURSE_TYPE_HEALTH : courseType; |
| | | } |
| | | |
| | | private Long requireVideoCourseId(Long courseId) { |
| | | return requireCourse(courseId).getId(); |
| | | } |
| | | |
| | | private HealthCourseVideo requireVideoById(Long id) { |
| | | HealthCourseVideo video = healthCourseVideoMapper.selectById(id); |
| | | if (video == null) { |
| | | throw new IllegalArgumentException("视频课程不存在"); |
| | | } |
| | | return video; |
| | | } |
| | | |
| | | private List<Long> resolveMatchedCourseIds(String courseKeyword, Long courseId) { |
| | | if (courseId != null) { |
| | | return Collections.singletonList(requireCourse(courseId).getId()); |
| | | } |
| | | List<HealthCourse> courses = healthCourseMapper.selectList(new LambdaQueryWrapper<HealthCourse>() |
| | | .eq(HealthCourse::getDeleted, 0) |
| | | .like(courseKeyword != null && !courseKeyword.trim().isEmpty(), HealthCourse::getTitle, courseKeyword == null ? null : courseKeyword.trim())); |
| | | if (courses.isEmpty()) { |
| | | return Collections.emptyList(); |
| | | } |
| | | List<Long> ids = new ArrayList<Long>(); |
| | | for (HealthCourse course : courses) { |
| | | ids.add(course.getId()); |
| | | } |
| | | return ids; |
| | | } |
| | | |
| | | private Map<Long, String> resolveCourseTitleMap(Collection<Long> courseIds) { |
| | | if (courseIds == null || courseIds.isEmpty()) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | List<HealthCourse> courses = healthCourseMapper.selectList(new LambdaQueryWrapper<HealthCourse>() |
| | | .in(HealthCourse::getId, courseIds) |
| | | .eq(HealthCourse::getDeleted, 0)); |
| | | Map<Long, String> map = new LinkedHashMap<Long, String>(); |
| | | for (HealthCourse course : courses) { |
| | | map.put(course.getId(), course.getTitle()); |
| | | } |
| | | return map; |
| | | } |
| | | |
| | | private List<Long> extractCourseIds(List<HealthCourseVideo> videos) { |
| | | Set<Long> ids = new LinkedHashSet<Long>(); |
| | | for (HealthCourseVideo video : videos) { |
| | | ids.add(video.getCourseId()); |
| | | } |
| | | return new ArrayList<Long>(ids); |
| | | } |
| | | |
| | | private boolean isImportRowEmpty(HealthCourseVideoImportRow row) { |
| | | return row.getCourseId() == null |
| | | && (row.getChapterName() == null || row.getChapterName().trim().isEmpty()) |
| | | && row.getSalePrice() == null |
| | | && row.getDurationSeconds() == null |
| | | && (row.getVideoUrl() == null || row.getVideoUrl().trim().isEmpty()) |
| | | && (row.getStatusText() == null || row.getStatusText().trim().isEmpty()); |
| | | } |
| | | |
| | | private void validateImportRow(HealthCourseVideoImportRow row, int rowNum) { |
| | | if (row.getCourseId() == null) { |
| | | throw new IllegalArgumentException("第" + rowNum + "行:关联课程ID不能为空"); |
| | | } |
| | | if (row.getCourseTypeText() != null && !row.getCourseTypeText().trim().isEmpty() && !"养生课程".equals(row.getCourseTypeText().trim())) { |
| | | throw new IllegalArgumentException("第" + rowNum + "行:课程类型仅支持养生课程"); |
| | | } |
| | | if (row.getChapterName() == null || row.getChapterName().trim().isEmpty()) { |
| | | throw new IllegalArgumentException("第" + rowNum + "行:小节名称不能为空"); |
| | | } |
| | | if (row.getSalePrice() != null && row.getSalePrice().compareTo(BigDecimal.ZERO) < 0) { |
| | | throw new IllegalArgumentException("第" + rowNum + "行:小节价格不能小于0"); |
| | | } |
| | | if (row.getDurationSeconds() == null || row.getDurationSeconds() < 0) { |
| | | throw new IllegalArgumentException("第" + rowNum + "行:时长必须大于等于0"); |
| | | } |
| | | if (row.getVideoUrl() == null || row.getVideoUrl().trim().isEmpty()) { |
| | | throw new IllegalArgumentException("第" + rowNum + "行:视频地址不能为空"); |
| | | } |
| | | try { |
| | | parseStatusText(row.getStatusText()); |
| | | } catch (IllegalArgumentException ex) { |
| | | throw new IllegalArgumentException("第" + rowNum + "行:" + ex.getMessage()); |
| | | } |
| | | } |
| | | |
| | | private Integer parseStatusText(String statusText) { |
| | | if (statusText == null || statusText.trim().isEmpty() || "启用".equals(statusText.trim())) { |
| | | return 1; |
| | | } |
| | | if ("禁用".equals(statusText.trim())) { |
| | | return 0; |
| | | } |
| | | throw new IllegalArgumentException("状态仅支持启用或禁用"); |
| | | } |
| | | } |