package com.tcm.training.admin.live.service;
|
|
import cn.idev.excel.FastExcel;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.tcm.training.admin.common.Jsons;
|
import com.tcm.training.admin.common.PageResponse;
|
import com.tcm.training.admin.live.dto.HealthCourseSaveRequest;
|
import com.tcm.training.admin.live.dto.HealthCourseStatusRequest;
|
import com.tcm.training.admin.live.dto.HealthCourseVideoSaveRequest;
|
import com.tcm.training.admin.live.entity.Expert;
|
import com.tcm.training.admin.live.entity.HealthCourse;
|
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.mapper.LiveCourseCategoryMapper;
|
import com.tcm.training.admin.live.vo.ExpertVO;
|
import com.tcm.training.admin.live.vo.HealthCourseDetailRow;
|
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.math.BigDecimal;
|
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 {
|
|
private final HealthCourseMapper healthCourseMapper;
|
private final HealthCourseVideoMapper healthCourseVideoMapper;
|
private final LiveCourseCategoryMapper liveCourseCategoryMapper;
|
private final ExpertMapper expertMapper;
|
|
public HealthCourseAppService(HealthCourseMapper healthCourseMapper,
|
HealthCourseVideoMapper healthCourseVideoMapper,
|
LiveCourseCategoryMapper liveCourseCategoryMapper,
|
ExpertMapper expertMapper) {
|
this.healthCourseMapper = healthCourseMapper;
|
this.healthCourseVideoMapper = healthCourseVideoMapper;
|
this.liveCourseCategoryMapper = liveCourseCategoryMapper;
|
this.expertMapper = expertMapper;
|
}
|
|
public PageResponse<HealthCourseListItemVO> page(int page,
|
int pageSize,
|
String keyword,
|
Long levelCategoryId,
|
Long difficultyCategoryId,
|
Integer status) {
|
IPage<HealthCourseListRow> result = healthCourseMapper.selectCoursePage(
|
new Page<HealthCourseListRow>(page, pageSize),
|
trimToNull(keyword),
|
levelCategoryId,
|
difficultyCategoryId,
|
status
|
);
|
List<HealthCourseListItemVO> list = new ArrayList<HealthCourseListItemVO>();
|
for (HealthCourseListRow row : result.getRecords()) {
|
list.add(toCourseListItem(row));
|
}
|
return PageResponse.of(list, result.getTotal(), result.getCurrent(), result.getSize());
|
}
|
|
public HealthCourseDetailVO detail(Long id) {
|
HealthCourseDetailRow row = healthCourseMapper.selectCourseDetail(id);
|
if (row == null) {
|
throw new IllegalArgumentException("课程不存在");
|
}
|
return toCourseDetail(row);
|
}
|
|
@Transactional
|
public HealthCourseDetailVO create(HealthCourseSaveRequest req) {
|
validateCategories(req.getLevelCategoryId(), req.getDifficultyCategoryId());
|
validateExpert(req.getExpertId());
|
|
HealthCourse course = new HealthCourse();
|
fillCourse(course, req);
|
course.setRealSalesCount(0);
|
course.setLearnUserCount(0);
|
course.setFavoriteCount(0);
|
course.setTotalDurationSeconds(0);
|
course.setVideoCount(0);
|
course.setSort(0);
|
course.setDeleted(0);
|
healthCourseMapper.insert(course);
|
return detail(course.getId());
|
}
|
|
@Transactional
|
public HealthCourseDetailVO update(Long id, HealthCourseSaveRequest req) {
|
validateCategories(req.getLevelCategoryId(), req.getDifficultyCategoryId());
|
validateExpert(req.getExpertId());
|
HealthCourse course = requireCourse(id);
|
fillCourse(course, req);
|
healthCourseMapper.updateById(course);
|
return detail(id);
|
}
|
|
@Transactional
|
public HealthCourseStatusVO patchStatus(Long id, HealthCourseStatusRequest req) {
|
HealthCourse course = requireCourse(id);
|
course.setStatus(req.getStatus());
|
healthCourseMapper.updateById(course);
|
return new HealthCourseStatusVO(id, req.getStatus());
|
}
|
|
@Transactional
|
public void delete(Long id) {
|
requireCourse(id);
|
List<HealthCourseVideo> videos = healthCourseVideoMapper.selectList(new LambdaQueryWrapper<HealthCourseVideo>()
|
.eq(HealthCourseVideo::getCourseId, id)
|
.eq(HealthCourseVideo::getDeleted, 0));
|
for (HealthCourseVideo video : videos) {
|
healthCourseVideoMapper.deleteById(video.getId());
|
}
|
healthCourseMapper.deleteById(id);
|
}
|
|
public void export(HttpServletResponse response,
|
String keyword,
|
Long levelCategoryId,
|
Long difficultyCategoryId,
|
Integer status) 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<HealthCourseListRow> rows = healthCourseMapper.selectCourseList(
|
trimToNull(keyword),
|
levelCategoryId,
|
difficultyCategoryId,
|
status
|
);
|
List<HealthCourseExportRow> exports = new ArrayList<HealthCourseExportRow>();
|
for (int i = 0; i < rows.size(); i++) {
|
HealthCourseListRow row = rows.get(i);
|
HealthCourseExportRow export = new HealthCourseExportRow();
|
export.setSerialNo(i + 1);
|
export.setTitle(defaultText(row.getTitle()));
|
export.setLevelName(defaultText(row.getLevelName()));
|
export.setDifficultyName(defaultText(row.getDifficultyName()));
|
export.setSalePrice(row.getSalePrice() == null ? BigDecimal.ZERO : row.getSalePrice());
|
export.setSalesCount(resolveSalesCount(row.getBaseSalesCount(), row.getRealSalesCount()));
|
export.setLearnUserCount(defaultNumber(row.getLearnUserCount()));
|
export.setTotalDuration(formatDuration(defaultNumber(row.getTotalDurationSeconds())));
|
export.setFavoriteCount(defaultNumber(row.getFavoriteCount()));
|
export.setExpertName(defaultText(row.getExpertName()));
|
export.setStatusText(statusText(row.getStatus()));
|
exports.add(export);
|
}
|
FastExcel.write(response.getOutputStream(), HealthCourseExportRow.class)
|
.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 priceType) {
|
requireCourse(courseId);
|
LambdaQueryWrapper<HealthCourseVideo> qw = new LambdaQueryWrapper<HealthCourseVideo>()
|
.eq(HealthCourseVideo::getCourseId, courseId)
|
.eq(HealthCourseVideo::getDeleted, 0)
|
.orderByAsc(HealthCourseVideo::getSort)
|
.orderByAsc(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);
|
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());
|
}
|
|
public HealthCourseVideoDetailVO detailVideo(Long courseId, Long videoId) {
|
return toVideoDetail(requireVideo(courseId, videoId));
|
}
|
|
@Transactional
|
public HealthCourseVideoDetailVO createVideo(Long courseId, HealthCourseVideoSaveRequest req) {
|
req.setCourseId(courseId);
|
return createVideoResource(req);
|
}
|
|
@Transactional
|
public HealthCourseVideoDetailVO updateVideo(Long courseId, Long videoId, HealthCourseVideoSaveRequest req) {
|
req.setCourseId(courseId);
|
return updateVideoResource(videoId, req);
|
}
|
|
@Transactional
|
public void deleteVideo(Long courseId, Long videoId) {
|
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) {
|
course.setTitle(req.getTitle().trim());
|
course.setLevelCategoryId(req.getLevelCategoryId());
|
course.setDifficultyCategoryId(req.getDifficultyCategoryId());
|
course.setExpertId(req.getExpertId());
|
course.setSalePrice(req.getSalePrice());
|
course.setSummary(trimToNull(req.getSummary()));
|
course.setTargetUsers(trimToNull(req.getTargetUsers()));
|
course.setCoverImg(req.getCoverImg().trim());
|
course.setBannerImgs(req.getBannerImgs());
|
course.setContent(req.getContent().trim());
|
course.setBaseSalesCount(req.getBaseSalesCount() == null ? 0 : req.getBaseSalesCount());
|
course.setStatus(req.getStatus());
|
course.setIsHomeRecommend(req.getIsHomeRecommend());
|
}
|
|
private void fillVideo(HealthCourseVideo video, HealthCourseVideoSaveRequest req) {
|
video.setCourseType(resolveCourseType(req.getCourseType()));
|
video.setChapterName(req.getChapterName().trim());
|
video.setVideoSourceType(req.getVideoSourceType());
|
video.setVideoUrl(req.getVideoUrl().trim());
|
video.setVideoFileName(trimToNull(req.getVideoFileName()));
|
video.setDurationSeconds(req.getDurationSeconds());
|
video.setSalePrice(req.getSalePrice());
|
video.setStatus(resolveVideoStatus(req.getStatus()));
|
}
|
|
private void validateCategories(Long levelCategoryId, Long difficultyCategoryId) {
|
LiveCourseCategory level = requireCategory(levelCategoryId, LiveCourseCategoryAppService.TYPE_LEVEL, "养生主题");
|
LiveCourseCategory difficulty = requireCategory(difficultyCategoryId, LiveCourseCategoryAppService.TYPE_DIFFICULTY, "难度");
|
if (level == null || difficulty == null) {
|
throw new IllegalArgumentException("分类不存在");
|
}
|
}
|
|
private LiveCourseCategory requireCategory(Long id, String type, String label) {
|
LiveCourseCategory category = liveCourseCategoryMapper.selectOne(new LambdaQueryWrapper<LiveCourseCategory>()
|
.eq(LiveCourseCategory::getId, id)
|
.eq(LiveCourseCategory::getType, type)
|
.eq(LiveCourseCategory::getDeleted, 0)
|
.last("limit 1"));
|
if (category == null) {
|
throw new IllegalArgumentException(label + "不存在");
|
}
|
return category;
|
}
|
|
private void validateExpert(Long expertId) {
|
Expert expert = expertMapper.selectOne(new LambdaQueryWrapper<Expert>()
|
.eq(Expert::getId, expertId)
|
.eq(Expert::getDeleted, 0)
|
.last("limit 1"));
|
if (expert == null) {
|
throw new IllegalArgumentException("授课专家不存在");
|
}
|
if (expert.getStatus() == null || expert.getStatus() != 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.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) {
|
HealthCourseVideo latest = healthCourseVideoMapper.selectOne(new LambdaQueryWrapper<HealthCourseVideo>()
|
.eq(HealthCourseVideo::getCourseId, courseId)
|
.eq(HealthCourseVideo::getDeleted, 0)
|
.orderByDesc(HealthCourseVideo::getSort)
|
.last("limit 1"));
|
if (latest == null || latest.getSort() == null) {
|
return 10;
|
}
|
return latest.getSort() + 10;
|
}
|
|
private void refreshCourseStats(Long courseId) {
|
HealthCourse course = requireCourse(courseId);
|
List<HealthCourseVideo> videos = healthCourseVideoMapper.selectList(new LambdaQueryWrapper<HealthCourseVideo>()
|
.eq(HealthCourseVideo::getCourseId, courseId)
|
.eq(HealthCourseVideo::getDeleted, 0));
|
int totalDurationSeconds = 0;
|
for (HealthCourseVideo video : videos) {
|
totalDurationSeconds += video.getDurationSeconds() == null ? 0 : video.getDurationSeconds();
|
}
|
course.setVideoCount(videos.size());
|
course.setTotalDurationSeconds(totalDurationSeconds);
|
healthCourseMapper.updateById(course);
|
}
|
|
private HealthCourse requireCourse(Long id) {
|
HealthCourse course = healthCourseMapper.selectById(id);
|
if (course == null) {
|
throw new IllegalArgumentException("课程不存在");
|
}
|
return course;
|
}
|
|
private HealthCourseVideo requireVideo(Long courseId, Long videoId) {
|
requireCourse(courseId);
|
HealthCourseVideo video = requireVideoById(videoId);
|
if (!courseId.equals(video.getCourseId())) {
|
throw new IllegalArgumentException("视频课程不属于当前课程");
|
}
|
return video;
|
}
|
|
private HealthCourseListItemVO toCourseListItem(HealthCourseListRow row) {
|
HealthCourseListItemVO vo = new HealthCourseListItemVO();
|
vo.setId(row.getId());
|
vo.setTitle(row.getTitle());
|
vo.setLevelName(row.getLevelName());
|
vo.setDifficultyName(row.getDifficultyName());
|
vo.setSalePrice(row.getSalePrice() == null ? BigDecimal.ZERO : row.getSalePrice());
|
vo.setSalesCount(resolveSalesCount(row.getBaseSalesCount(), row.getRealSalesCount()));
|
vo.setLearnUserCount(defaultNumber(row.getLearnUserCount()));
|
vo.setTotalDurationSeconds(defaultNumber(row.getTotalDurationSeconds()));
|
vo.setFavoriteCount(defaultNumber(row.getFavoriteCount()));
|
vo.setExpert(new ExpertVO(row.getExpertId(), row.getExpertName(), row.getExpertAvatar()));
|
vo.setCoverImg(row.getCoverImg());
|
vo.setStatus(row.getStatus());
|
vo.setIsHomeRecommend(row.getIsHomeRecommend());
|
vo.setCreatedAt(row.getCreatedAt());
|
return vo;
|
}
|
|
private HealthCourseDetailVO toCourseDetail(HealthCourseDetailRow row) {
|
HealthCourseDetailVO vo = new HealthCourseDetailVO();
|
vo.setId(row.getId());
|
vo.setTitle(row.getTitle());
|
vo.setLevelCategoryId(row.getLevelCategoryId());
|
vo.setLevelName(row.getLevelName());
|
vo.setDifficultyCategoryId(row.getDifficultyCategoryId());
|
vo.setDifficultyName(row.getDifficultyName());
|
vo.setExpert(new ExpertVO(row.getExpertId(), row.getExpertName(), row.getExpertAvatar()));
|
vo.setSalePrice(row.getSalePrice() == null ? BigDecimal.ZERO : row.getSalePrice());
|
vo.setSummary(row.getSummary());
|
vo.setTargetUsers(row.getTargetUsers());
|
vo.setCoverImg(row.getCoverImg());
|
vo.setBannerImgs(Jsons.parseStringList(row.getBannerImgs()));
|
vo.setContent(row.getContent());
|
vo.setBaseSalesCount(defaultNumber(row.getBaseSalesCount()));
|
vo.setRealSalesCount(defaultNumber(row.getRealSalesCount()));
|
vo.setSalesCount(resolveSalesCount(row.getBaseSalesCount(), row.getRealSalesCount()));
|
vo.setLearnUserCount(defaultNumber(row.getLearnUserCount()));
|
vo.setFavoriteCount(defaultNumber(row.getFavoriteCount()));
|
vo.setTotalDurationSeconds(defaultNumber(row.getTotalDurationSeconds()));
|
vo.setVideoCount(defaultNumber(row.getVideoCount()));
|
vo.setStatus(row.getStatus());
|
vo.setIsHomeRecommend(row.getIsHomeRecommend());
|
vo.setSort(row.getSort());
|
vo.setCreatedAt(row.getCreatedAt());
|
vo.setUpdatedAt(row.getUpdatedAt());
|
return vo;
|
}
|
|
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.setVideoSourceType(item.getVideoSourceType());
|
vo.setVideoUrl(item.getVideoUrl());
|
vo.setVideoFileName(item.getVideoFileName());
|
vo.setDurationSeconds(item.getDurationSeconds());
|
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.setVideoSourceType(item.getVideoSourceType());
|
vo.setVideoUrl(item.getVideoUrl());
|
vo.setVideoFileName(item.getVideoFileName());
|
vo.setDurationSeconds(item.getDurationSeconds());
|
vo.setSalePrice(item.getSalePrice() == null ? BigDecimal.ZERO : item.getSalePrice());
|
vo.setWatchCount(0);
|
vo.setSort(item.getSort());
|
vo.setStatus(item.getStatus());
|
vo.setCreatedAt(item.getCreatedAt());
|
vo.setUpdatedAt(item.getUpdatedAt());
|
return vo;
|
}
|
|
private Integer resolveSalesCount(Integer baseSalesCount, Integer realSalesCount) {
|
return defaultNumber(baseSalesCount) + defaultNumber(realSalesCount);
|
}
|
|
private Integer defaultNumber(Integer value) {
|
return value == null ? 0 : value;
|
}
|
|
private String formatDuration(Integer seconds) {
|
int totalSeconds = seconds == null ? 0 : seconds;
|
int minutes = totalSeconds / 60;
|
int remainSeconds = totalSeconds % 60;
|
return minutes + ":" + String.format("%02d", remainSeconds);
|
}
|
|
private String statusText(Integer status) {
|
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;
|
}
|
|
private String trimToNull(String text) {
|
if (text == null) {
|
return null;
|
}
|
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("状态仅支持启用或禁用");
|
}
|
}
|