Compare commits

..

No commits in common. "a2751d7aa5345583e8ba22be88765bbd12b3bce4" and "f425209abe2805b9828888afd79c0090f9a6cdf5" have entirely different histories.

9 changed files with 1272 additions and 1637 deletions

View File

@ -1,65 +0,0 @@
# 开发日志 - 2026-03-19
## Service 层重构
### 背景
根据项目统一开发规范要求Service 层应采用 **接口 + 实现类** 的结构:
```
service/
├── XxxService.java ← 接口,继承 IService<Entity>
└── impl/
└── XxxServiceImpl.java ← 实现类,继承 ServiceImpl 并实现接口
```
### 重构内容
将以下 5 个服务类从实现类拆分为接口 + 实现类结构:
| 服务类 | 接口文件 | 实现类文件 | 状态 |
|--------|---------|-----------|------|
| `ThemeService` | `service/ThemeService.java` | `service/impl/ThemeServiceImpl.java` | ✅ |
| `ResourceLibraryService` | `service/ResourceLibraryService.java` | `service/impl/ResourceLibraryServiceImpl.java` | ✅ |
| `CourseLessonService` | `service/CourseLessonService.java` | `service/impl/CourseLessonServiceImpl.java` | ✅ |
| `CourseCollectionService` | `service/CourseCollectionService.java` | `service/impl/CourseCollectionServiceImpl.java` | ✅ |
| `FileStorageService` | 无需拆分(纯工具类) | 保持原结构 | ✅ |
### 重构详情
#### 1. ThemeService
- **接口方法**`findAll()`, `findById()`, `create()`, `update()`, `delete()`, `reorder()`
- **实现类**`ThemeServiceImpl` 继承 `ServiceImpl<ThemeMapper, Theme>` 并实现 `ThemeService` 接口
#### 2. ResourceLibraryService
- **接口方法**`findAllLibraries()`, `findLibraryById()`, `createLibrary()`, `updateLibrary()`, `deleteLibrary()`, `findAllItems()`, `findItemById()`, `createItem()`, `updateItem()`, `deleteItem()`, `batchDeleteItems()`, `getStats()`
- **实现类**`ResourceLibraryServiceImpl` 继承 `ServiceImpl<ResourceLibraryMapper, ResourceLibrary>` 并实现 `ResourceLibraryService` 接口
#### 3. CourseLessonService
- **接口方法**`findByCourseId()`, `findById()`, `findByType()`, `create()`, `update()`, `delete()`, `reorder()`, `findSteps()`, `createStep()`, `updateStep()`, `deleteStep()`, `reorderSteps()`, `findCourseLessonsForTeacher()`
- **实现类**`CourseLessonServiceImpl` 继承 `ServiceImpl<CourseLessonMapper, CourseLesson>` 并实现 `CourseLessonService` 接口
#### 4. CourseCollectionService
- **接口方法**`findTenantCollections()`, `getCollectionDetail()`, `pageCollections()`, `getPackagesByCollection()`, `createCollection()`, `setCollectionPackages()`, `updateCollection()`, `deleteCollection()`, `publishCollection()`, `archiveCollection()`, `republishCollection()`, `withdrawCollection()`, `submitCollection()`, `rejectCollection()`, `renewTenantCollection()`
- **实现类**`CourseCollectionServiceImpl` 继承 `ServiceImpl<CourseCollectionMapper, CourseCollection>` 并实现 `CourseCollectionService` 接口
- **依赖注入**:注入 `CourseLessonService` 用于查询课程环节
### 验证结果
```bash
export JAVA_HOME="/f/Java/jdk-17"
mvn compile -DskipTests
```
**编译结果**:✅ BUILD SUCCESS
### 影响范围
- Controller 层引用保持不变Spring 自动注入接口实现)
- 其他服务层调用保持不变
- 无数据库变更
- 无 API 变更
### 备注
- `FileStorageService` 是纯工具类服务,不涉及数据库操作,无需继承 `IService`,保持当前结构即可

View File

@ -1,93 +1,625 @@
package com.reading.platform.service;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.reading.platform.common.enums.CourseStatus;
import com.reading.platform.common.enums.TenantPackageStatus;
import com.reading.platform.common.exception.BusinessException;
import com.reading.platform.common.response.PageResult;
import com.reading.platform.dto.response.CourseCollectionResponse;
import com.reading.platform.dto.response.CoursePackageResponse;
import com.reading.platform.entity.CoursePackage;
import com.reading.platform.entity.CourseCollection;
import com.reading.platform.entity.CourseCollectionPackage;
import com.reading.platform.entity.CoursePackageCourse;
import com.reading.platform.entity.CourseLesson;
import com.reading.platform.entity.TenantPackage;
import com.reading.platform.mapper.CourseCollectionMapper;
import com.reading.platform.mapper.CourseCollectionPackageMapper;
import com.reading.platform.mapper.CoursePackageMapper;
import com.reading.platform.mapper.CoursePackageCourseMapper;
import com.reading.platform.mapper.TenantPackageMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* 课程套餐服务接口两层结构 - 最上层
* 课程套餐服务两层结构-最上层
*/
public interface CourseCollectionService extends IService<CourseCollection> {
@Slf4j
@Service
@RequiredArgsConstructor
public class CourseCollectionService extends ServiceImpl<CourseCollectionMapper, CourseCollection> {
private final CourseCollectionMapper collectionMapper;
private final CourseCollectionPackageMapper collectionPackageMapper;
private final CoursePackageMapper packageMapper;
private final TenantPackageMapper tenantPackageMapper;
private final CoursePackageCourseMapper packageCoursePackageMapper;
private final CoursePackageMapper courseMapper;
private final CourseLessonService courseLessonService;
/**
* 查询租户的课程套餐列表
*/
List<CourseCollectionResponse> findTenantCollections(Long tenantId);
public List<CourseCollectionResponse> findTenantCollections(Long tenantId) {
log.info("查询租户课程套餐tenantId={}", tenantId);
// 查询租户的课程套餐关联
List<TenantPackage> tenantPackages = tenantPackageMapper.selectList(
new LambdaQueryWrapper<TenantPackage>()
.eq(TenantPackage::getTenantId, tenantId)
.eq(TenantPackage::getStatus, TenantPackageStatus.ACTIVE.getCode())
.isNotNull(TenantPackage::getCollectionId)
.orderByDesc(TenantPackage::getCreatedAt)
);
log.info("查询到{}条租户套餐关联记录", tenantPackages.size());
for (TenantPackage tp : tenantPackages) {
log.info(" - collection_id: {}, start_date: {}, end_date: {}",
tp.getCollectionId(), tp.getStartDate(), tp.getEndDate());
}
// 获取套餐详情并转换为响应对象
List<CourseCollectionResponse> result = tenantPackages.stream()
.map(tp -> {
CourseCollection collection = collectionMapper.selectById(tp.getCollectionId());
if (collection == null) {
log.warn("套餐不存在collection_id={}", tp.getCollectionId());
return null;
}
// 过滤下架状态的课程套餐
if (CourseStatus.ARCHIVED.getCode().equals(collection.getStatus())) {
log.warn("套餐已下架collection_id={}", tp.getCollectionId());
return null;
}
log.info("返回套餐: id={}, name={}, package_count={}",
collection.getId(), collection.getName(), collection.getPackageCount());
CourseCollectionResponse response = toResponse(collection);
// 设置租户套餐的额外信息如果有
response.setStartDate(tp.getStartDate());
response.setEndDate(tp.getEndDate());
return response;
})
.filter(r -> r != null)
.collect(Collectors.toList());
log.info("查询到{}个课程套餐", result.size());
return result;
}
/**
* 获取课程套餐详情包含课程包列表
*/
CourseCollectionResponse getCollectionDetail(Long collectionId);
public CourseCollectionResponse getCollectionDetail(Long collectionId) {
log.info("获取课程套餐详情collectionId={}", collectionId);
CourseCollection collection = collectionMapper.selectById(collectionId);
if (collection == null) {
log.warn("课程套餐不存在collectionId={}", collectionId);
return null;
}
return toResponse(collection);
}
/**
* 分页查询课程套餐
*/
Page<CourseCollectionResponse> pageCollections(Integer pageNum, Integer pageSize, String status);
public Page<CourseCollectionResponse> pageCollections(Integer pageNum, Integer pageSize, String status) {
log.info("分页查询课程套餐pageNum={}, pageSize={}, status={}", pageNum, pageSize, status);
LambdaQueryWrapper<CourseCollection> wrapper = new LambdaQueryWrapper<>();
if (StringUtils.hasText(status)) {
wrapper.eq(CourseCollection::getStatus, status);
}
wrapper.orderByDesc(CourseCollection::getCreatedAt);
Page<CourseCollection> page = collectionMapper.selectPage(
new Page<>(pageNum, pageSize),
wrapper
);
// 转换为响应对象
List<CourseCollectionResponse> responses = page.getRecords().stream()
.map(this::toResponse)
.collect(Collectors.toList());
Page<CourseCollectionResponse> result = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
result.setRecords(responses);
return result;
}
/**
* 获取课程套餐下的课程包列表
*/
List<CoursePackageResponse> getPackagesByCollection(Long collectionId);
public List<CoursePackageResponse> getPackagesByCollection(Long collectionId) {
log.info("获取课程套餐的课程包列表collectionId={}", collectionId);
// 查询关联关系
List<CourseCollectionPackage> associations = collectionPackageMapper.selectList(
new LambdaQueryWrapper<CourseCollectionPackage>()
.eq(CourseCollectionPackage::getCollectionId, collectionId)
.orderByAsc(CourseCollectionPackage::getSortOrder)
);
if (associations.isEmpty()) {
return new ArrayList<>();
}
// 获取课程包详情
List<Long> packageIds = associations.stream()
.map(CourseCollectionPackage::getPackageId)
.collect(Collectors.toList());
List<CoursePackage> packages = packageMapper.selectList(
new LambdaQueryWrapper<CoursePackage>()
.in(CoursePackage::getId, packageIds)
.eq(CoursePackage::getStatus, CourseStatus.PUBLISHED.getCode())
);
// 转换为响应对象并设置排序号
List<CoursePackageResponse> result = packages.stream()
.map(pkg -> {
CoursePackageResponse response = toPackageResponse(pkg);
// 设置排序号
associations.stream()
.filter(a -> a.getPackageId().equals(pkg.getId()))
.findFirst()
.ifPresent(a -> response.setSortOrder(a.getSortOrder()));
return response;
})
.collect(Collectors.toList());
log.info("查询到{}个课程包", result.size());
return result;
}
/**
* 创建课程套餐
*/
CourseCollectionResponse createCollection(String name, String description, Long price,
Long discountPrice, String discountType, String[] gradeLevels);
@Transactional(rollbackFor = Exception.class)
public CourseCollectionResponse createCollection(String name, String description, Long price,
Long discountPrice, String discountType, String[] gradeLevels) {
log.info("创建课程套餐name={}", name);
CourseCollection collection = new CourseCollection();
collection.setName(name);
collection.setDescription(description);
collection.setPrice(price);
collection.setDiscountPrice(discountPrice);
collection.setDiscountType(discountType);
// 将数组转为 JSON 字符串存储到 JSON 字段
collection.setGradeLevels(JSON.toJSONString(gradeLevels));
collection.setPackageCount(0);
collection.setStatus(CourseStatus.DRAFT.getCode());
collectionMapper.insert(collection);
log.info("课程套餐创建成功id={}", collection.getId());
return toResponse(collection);
}
/**
* 设置课程套餐的课程包
*/
void setCollectionPackages(Long collectionId, List<Long> packageIds);
@Transactional(rollbackFor = Exception.class)
public void setCollectionPackages(Long collectionId, List<Long> packageIds) {
log.info("设置课程套餐的课程包collectionId={}, packageCount={}", collectionId, packageIds.size());
// 验证课程套餐是否存在
CourseCollection collection = collectionMapper.selectById(collectionId);
if (collection == null) {
throw new BusinessException("课程套餐不存在");
}
// 去重保持首次出现顺序避免 uk_collection_package 唯一约束冲突
List<Long> distinctIds = packageIds.stream().distinct().collect(Collectors.toList());
// 验证课程包是否存在应用层外键约束
if (!distinctIds.isEmpty()) {
List<CoursePackage> packages = packageMapper.selectList(
new LambdaQueryWrapper<CoursePackage>()
.in(CoursePackage::getId, distinctIds)
);
if (packages.size() != distinctIds.size()) {
throw new BusinessException("存在无效的课程包 ID");
}
}
// 删除旧的关联
collectionPackageMapper.delete(
new LambdaQueryWrapper<CourseCollectionPackage>()
.eq(CourseCollectionPackage::getCollectionId, collectionId)
);
// 创建新的关联
for (int i = 0; i < distinctIds.size(); i++) {
CourseCollectionPackage association = new CourseCollectionPackage();
association.setCollectionId(collectionId);
association.setPackageId(distinctIds.get(i));
association.setSortOrder(i + 1);
collectionPackageMapper.insert(association);
}
// 更新课程包数量复用前面已验证的 collection 变量
collection.setPackageCount(distinctIds.size());
collectionMapper.updateById(collection);
log.info("课程套餐的课程包设置完成");
}
/**
* 更新课程套餐
*/
CourseCollectionResponse updateCollection(Long id, String name, String description, Long price,
Long discountPrice, String discountType, String[] gradeLevels);
@Transactional(rollbackFor = Exception.class)
public CourseCollectionResponse updateCollection(Long id, String name, String description, Long price,
Long discountPrice, String discountType, String[] gradeLevels) {
log.info("更新课程套餐id={}", id);
CourseCollection collection = collectionMapper.selectById(id);
if (collection == null) {
throw new IllegalArgumentException("课程套餐不存在");
}
collection.setName(name);
collection.setDescription(description);
collection.setPrice(price);
collection.setDiscountPrice(discountPrice);
collection.setDiscountType(discountType);
// 将数组转为 JSON 字符串存储到 JSON 字段
collection.setGradeLevels(JSON.toJSONString(gradeLevels));
collectionMapper.updateById(collection);
log.info("课程套餐更新成功id={}", id);
return toResponse(collection);
}
/**
* 删除课程套餐
*/
void deleteCollection(Long id);
@Transactional(rollbackFor = Exception.class)
public void deleteCollection(Long id) {
log.info("删除课程套餐id={}", id);
// 检查是否有租户正在使用此课程套餐
Long tenantCount = tenantPackageMapper.selectCount(
new LambdaQueryWrapper<TenantPackage>()
.eq(TenantPackage::getCollectionId, id)
.eq(TenantPackage::getStatus, TenantPackageStatus.ACTIVE)
);
if (tenantCount > 0) {
log.warn("删除课程套餐失败,有 {} 个租户正在使用此套餐id={}", tenantCount, id);
throw new BusinessException("" + tenantCount + " 个租户正在使用此课程套餐,无法删除");
}
// 清理所有状态的租户套餐关联记录包括非活跃状态
List<TenantPackage> allTenantPackages = tenantPackageMapper.selectList(
new LambdaQueryWrapper<TenantPackage>()
.eq(TenantPackage::getCollectionId, id)
);
if (!allTenantPackages.isEmpty()) {
tenantPackageMapper.delete(
new LambdaQueryWrapper<TenantPackage>()
.eq(TenantPackage::getCollectionId, id)
);
log.info("已清理 {} 条租户套餐关联记录", allTenantPackages.size());
}
// 删除课程套餐与课程包的关联关系
List<CourseCollectionPackage> collectionPackages = collectionPackageMapper.selectList(
new LambdaQueryWrapper<CourseCollectionPackage>()
.eq(CourseCollectionPackage::getCollectionId, id)
);
if (!collectionPackages.isEmpty()) {
collectionPackageMapper.delete(
new LambdaQueryWrapper<CourseCollectionPackage>()
.eq(CourseCollectionPackage::getCollectionId, id)
);
log.info("已清理 {} 条课程包关联记录", collectionPackages.size());
}
// 删除课程套餐
collectionMapper.deleteById(id);
log.info("课程套餐删除成功id={}", id);
}
/**
* 发布课程套餐
*/
void publishCollection(Long id);
@Transactional(rollbackFor = Exception.class)
public void publishCollection(Long id) {
log.info("发布课程套餐id={}", id);
CourseCollection collection = collectionMapper.selectById(id);
if (collection == null) {
throw new IllegalArgumentException("课程套餐不存在");
}
collection.setStatus(CourseStatus.PUBLISHED.getCode());
collection.setPublishedAt(LocalDateTime.now());
collectionMapper.updateById(collection);
log.info("课程套餐发布成功id={}", id);
}
/**
* 下架课程套餐
*/
void archiveCollection(Long id);
@Transactional(rollbackFor = Exception.class)
public void archiveCollection(Long id) {
log.info("下架课程套餐id={}", id);
CourseCollection collection = collectionMapper.selectById(id);
if (collection == null) {
throw new IllegalArgumentException("课程套餐不存在");
}
collection.setStatus(CourseStatus.ARCHIVED.getCode());
collectionMapper.updateById(collection);
log.info("课程套餐下架成功id={}", id);
}
/**
* 重新发布课程套餐
*/
void republishCollection(Long id);
@Transactional(rollbackFor = Exception.class)
public void republishCollection(Long id) {
log.info("重新发布课程套餐id={}", id);
CourseCollection collection = collectionMapper.selectById(id);
if (collection == null) {
throw new IllegalArgumentException("课程套餐不存在");
}
collection.setStatus(CourseStatus.PUBLISHED.getCode());
collection.setPublishedAt(LocalDateTime.now());
collectionMapper.updateById(collection);
log.info("课程套餐重新发布成功id={}", id);
}
/**
* 撤销审核
*/
void withdrawCollection(Long id);
@Transactional(rollbackFor = Exception.class)
public void withdrawCollection(Long id) {
log.info("撤销课程套餐审核id={}", id);
CourseCollection collection = collectionMapper.selectById(id);
if (collection == null) {
throw new IllegalArgumentException("课程套餐不存在");
}
collection.setStatus(CourseStatus.DRAFT.getCode());
collectionMapper.updateById(collection);
log.info("课程套餐审核撤销成功id={}", id);
}
/**
* 提交课程套餐审核
*/
void submitCollection(Long id);
@Transactional(rollbackFor = Exception.class)
public void submitCollection(Long id) {
log.info("提交课程套餐审核id={}", id);
CourseCollection collection = collectionMapper.selectById(id);
if (collection == null) {
throw new IllegalArgumentException("课程套餐不存在");
}
// 检查是否包含课程包
if (collection.getPackageCount() == null || collection.getPackageCount() == 0) {
throw new BusinessException("课程套餐必须包含至少一个课程包");
}
collection.setStatus(CourseStatus.PENDING.getCode());
collection.setSubmittedAt(LocalDateTime.now());
collectionMapper.updateById(collection);
log.info("课程套餐提交审核成功id={}", id);
}
/**
* 审核驳回课程套餐
*/
void rejectCollection(Long id, String comment);
@Transactional(rollbackFor = Exception.class)
public void rejectCollection(Long id, String comment) {
log.info("审核驳回课程套餐id={}, comment={}", id, comment);
CourseCollection collection = collectionMapper.selectById(id);
if (collection == null) {
throw new IllegalArgumentException("课程套餐不存在");
}
collection.setStatus(CourseStatus.REJECTED.getCode());
collection.setReviewedAt(LocalDateTime.now());
collection.setReviewComment(comment);
collectionMapper.updateById(collection);
log.info("课程套餐审核驳回成功id={}", id);
}
/**
* 续费租户课程套餐
*/
void renewTenantCollection(Long tenantId, Long collectionId, LocalDate endDate, Long pricePaid);
@Transactional(rollbackFor = Exception.class)
public void renewTenantCollection(Long tenantId, Long collectionId, java.time.LocalDate endDate, Long pricePaid) {
log.info("续费租户课程套餐tenantId={}, collectionId={}, endDate={}, pricePaid={}", tenantId, collectionId, endDate, pricePaid);
// 查询现有租户套餐关联
List<TenantPackage> existingPackages = tenantPackageMapper.selectList(
new LambdaQueryWrapper<TenantPackage>()
.eq(TenantPackage::getTenantId, tenantId)
.eq(TenantPackage::getCollectionId, collectionId)
);
if (!existingPackages.isEmpty()) {
// 更新现有记录
TenantPackage existing = existingPackages.get(0);
existing.setEndDate(endDate);
existing.setStatus(TenantPackageStatus.ACTIVE);
if (pricePaid != null) {
existing.setPricePaid(pricePaid);
}
existing.setUpdatedAt(LocalDateTime.now());
tenantPackageMapper.updateById(existing);
log.info("租户课程套餐续费成功tenantId={}, collectionId={}", tenantId, collectionId);
} else {
// 查询课程套餐信息
CourseCollection collection = collectionMapper.selectById(collectionId);
if (collection == null) {
throw new IllegalArgumentException("课程套餐不存在");
}
// 创建新记录
TenantPackage tp = new TenantPackage();
tp.setTenantId(tenantId);
tp.setCollectionId(collectionId);
tp.setStartDate(java.time.LocalDate.now());
tp.setEndDate(endDate);
tp.setStatus(TenantPackageStatus.ACTIVE);
tp.setPricePaid(pricePaid != null ? pricePaid :
(collection.getDiscountPrice() != null ? collection.getDiscountPrice() : collection.getPrice()));
tp.setCreatedAt(LocalDateTime.now());
tenantPackageMapper.insert(tp);
log.info("租户课程套餐新办成功tenantId={}, collectionId={}", tenantId, collectionId);
}
}
/**
* 解析适用年级数据库存储为 JSON 数组 ["小班","中班","大班"]需正确解析
*/
private String[] parseGradeLevels(String gradeLevels) {
if (!StringUtils.hasText(gradeLevels)) {
return new String[0];
}
try {
if (gradeLevels.trim().startsWith("[")) {
return JSON.parseArray(gradeLevels, String.class).toArray(new String[0]);
}
// 兼容旧数据逗号分隔格式
return gradeLevels.split(",");
} catch (Exception e) {
log.warn("解析 gradeLevels 失败: {}", gradeLevels, e);
return new String[0];
}
}
/**
* 转换为响应对象
*/
private CourseCollectionResponse toResponse(CourseCollection collection) {
// 获取课程包列表
List<CourseCollectionResponse.CoursePackageItem> packages = getPackagesByCollection(collection.getId()).stream()
.map(pkg -> {
CourseCollectionResponse.CoursePackageItem item = new CourseCollectionResponse.CoursePackageItem();
item.setId(pkg.getId());
item.setName(pkg.getName());
item.setDescription(pkg.getDescription());
item.setGradeLevels(pkg.getGradeLevels() != null ? pkg.getGradeLevels() : new String[0]);
item.setCourseCount(pkg.getCourseCount() != null ? pkg.getCourseCount() : 0);
item.setSortOrder(pkg.getSortOrder());
return item;
})
.collect(Collectors.toList());
return CourseCollectionResponse.builder()
.id(collection.getId())
.name(collection.getName())
.description(collection.getDescription())
.price(collection.getPrice())
.discountPrice(collection.getDiscountPrice())
.discountType(collection.getDiscountType())
.gradeLevels(parseGradeLevels(collection.getGradeLevels()))
.packageCount(collection.getPackageCount())
.status(collection.getStatus())
.submittedAt(collection.getSubmittedAt())
.submittedBy(collection.getSubmittedBy())
.reviewedAt(collection.getReviewedAt())
.reviewedBy(collection.getReviewedBy())
.reviewComment(collection.getReviewComment())
.publishedAt(collection.getPublishedAt())
.createdAt(collection.getCreatedAt())
.updatedAt(collection.getUpdatedAt())
.packages(packages)
.build();
}
/**
* 转换为课程包响应对象包含课程列表和排课计划参考
*/
private CoursePackageResponse toPackageResponse(CoursePackage pkg) {
// 解析 gradeTagsCoursePackage 使用 gradeTags
String[] gradeLevelsArray = new String[0];
if (pkg.getGradeTags() != null && !pkg.getGradeTags().isEmpty()) {
try {
// gradeTags JSON 数组格式尝试解析
if (pkg.getGradeTags().startsWith("[")) {
gradeLevelsArray = com.alibaba.fastjson2.JSON.parseArray(pkg.getGradeTags(), String.class).toArray(new String[0]);
} else {
gradeLevelsArray = pkg.getGradeTags().split(",");
}
} catch (Exception e) {
gradeLevelsArray = new String[0];
}
}
List<CoursePackageResponse.CoursePackageCourseItem> courseItems = new ArrayList<>();
// 查询课程包关联的课程环节
List<CourseLesson> lessons = courseLessonService.findByCourseId(pkg.getId());
if (!lessons.isEmpty()) {
courseItems = lessons.stream()
.map(lesson -> {
CoursePackageResponse.CoursePackageCourseItem item = new CoursePackageResponse.CoursePackageCourseItem();
item.setId(lesson.getId());
item.setName(lesson.getName());
item.setGradeLevel(null); // 课程环节没有年级字段
item.setSortOrder(lesson.getSortOrder());
item.setScheduleRefData(pkg.getScheduleRefData());
item.setLessonType(lesson.getLessonType());
return item;
})
.collect(Collectors.toList());
}
return CoursePackageResponse.builder()
.id(pkg.getId())
.name(pkg.getName())
.description(pkg.getDescription())
.price(null) // CoursePackage 没有价格字段
.discountPrice(null)
.discountType(null)
.gradeLevels(gradeLevelsArray)
.courseCount(lessons.size()) // 使用课程环节数量
.status(pkg.getStatus())
.submittedAt(pkg.getSubmittedAt())
.submittedBy(pkg.getSubmittedBy())
.reviewedAt(pkg.getReviewedAt())
.reviewedBy(pkg.getReviewedBy())
.reviewComment(pkg.getReviewComment())
.publishedAt(pkg.getPublishedAt())
.createdAt(pkg.getCreatedAt())
.updatedAt(pkg.getUpdatedAt())
.courses(courseItems)
.build();
}
}

View File

@ -1,87 +1,463 @@
package com.reading.platform.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.reading.platform.entity.CourseLesson;
import com.reading.platform.entity.LessonStep;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.reading.platform.common.enums.TenantPackageStatus;
import com.reading.platform.common.exception.BusinessException;
import com.reading.platform.entity.*;
import com.reading.platform.mapper.*;
import com.reading.platform.mapper.TenantCourseMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/**
* 课程环节服务接口
* 课程环节服务
*/
public interface CourseLessonService extends IService<CourseLesson> {
@Slf4j
@Service
@RequiredArgsConstructor
public class CourseLessonService extends ServiceImpl<CourseLessonMapper, CourseLesson> {
private final CourseLessonMapper courseLessonMapper;
private final LessonStepMapper lessonStepMapper;
private final LessonStepResourceMapper lessonStepResourceMapper;
private final TenantCourseMapper tenantCourseMapper;
private final TenantPackageMapper tenantPackageMapper;
/**
* 查询课程的所有环节
*/
List<CourseLesson> findByCourseId(Long courseId);
public List<CourseLesson> findByCourseId(Long courseId) {
log.info("查询课程的所有环节courseId={}", courseId);
List<CourseLesson> result = courseLessonMapper.selectList(
new LambdaQueryWrapper<CourseLesson>()
.eq(CourseLesson::getCourseId, courseId)
.orderByAsc(CourseLesson::getSortOrder)
);
log.info("查询课程环节成功courseId={}, count={}", courseId, result.size());
return result;
}
/**
* 查询课程环节详情
*/
CourseLesson findById(Long id);
public CourseLesson findById(Long id) {
log.info("查询课程环节详情id={}", id);
CourseLesson lesson = courseLessonMapper.selectById(id);
if (lesson == null) {
log.warn("课程环节不存在id={}", id);
throw new BusinessException("课程环节不存在");
}
return lesson;
}
/**
* lessonType 查询包含该类型环节的课程 ID 列表
* 兼容多种存储格式SOCIAL/SOCIETY/DOMAIN_SOCIALINTRODUCTION/INTRO
*/
public List<Long> findCourseIdsByLessonType(String lessonType) {
List<String> typesToMatch = resolveLessonTypeVariants(lessonType);
if (typesToMatch.isEmpty()) {
return Collections.emptyList();
}
LambdaQueryWrapper<CourseLesson> wrapper = new LambdaQueryWrapper<CourseLesson>()
.select(CourseLesson::getCourseId);
if (typesToMatch.size() == 1) {
wrapper.eq(CourseLesson::getLessonType, typesToMatch.get(0));
} else {
wrapper.in(CourseLesson::getLessonType, typesToMatch);
}
return courseLessonMapper.selectList(wrapper).stream()
.map(CourseLesson::getCourseId)
.distinct()
.collect(Collectors.toList());
}
/**
* 将前端传入的 lessonType 解析为数据库中可能存储的多种格式
*/
private List<String> resolveLessonTypeVariants(String lessonType) {
if (lessonType == null || lessonType.isBlank()) {
return Collections.emptyList();
}
return switch (lessonType.toUpperCase()) {
case "SOCIAL" -> Arrays.asList("SOCIAL", "SOCIETY", "DOMAIN_SOCIAL");
case "SOCIETY" -> Arrays.asList("SOCIAL", "SOCIETY", "DOMAIN_SOCIAL");
case "SCIENCE" -> Arrays.asList("SCIENCE", "DOMAIN_SCIENCE");
case "LANGUAGE" -> Arrays.asList("LANGUAGE", "DOMAIN_LANGUAGE");
case "HEALTH" -> Arrays.asList("HEALTH", "DOMAIN_HEALTH");
case "ART" -> Arrays.asList("ART", "DOMAIN_ART");
case "INTRODUCTION" -> Arrays.asList("INTRODUCTION", "INTRO");
case "INTRO" -> Arrays.asList("INTRODUCTION", "INTRO");
case "COLLECTIVE" -> Collections.singletonList("COLLECTIVE");
default -> Collections.singletonList(lessonType);
};
}
/**
* 按类型查询课程环节
*/
CourseLesson findByType(Long courseId, String lessonType);
public CourseLesson findByType(Long courseId, String lessonType) {
log.info("按类型查询课程环节courseId={}, lessonType={}", courseId, lessonType);
CourseLesson result = courseLessonMapper.selectOne(
new LambdaQueryWrapper<CourseLesson>()
.eq(CourseLesson::getCourseId, courseId)
.eq(CourseLesson::getLessonType, lessonType)
);
if (result != null) {
log.info("查询课程环节成功courseId={}, lessonType={}, id={}", courseId, lessonType, result.getId());
}
return result;
}
/**
* 创建课程环节
*/
CourseLesson create(Long courseId, String lessonType, String name, String description,
Integer duration, String videoPath, String videoName,
String pptPath, String pptName, String pdfPath, String pdfName,
String objectives, String preparation, String extension,
String reflection, String assessmentData, Boolean useTemplate);
@Transactional(rollbackFor = Exception.class)
public CourseLesson create(Long courseId, String lessonType, String name, String description,
Integer duration, String videoPath, String videoName,
String pptPath, String pptName, String pdfPath, String pdfName,
String objectives, String preparation, String extension,
String reflection, String assessmentData, Boolean useTemplate) {
log.info("创建课程环节courseId={}, lessonType={}, name={}", courseId, lessonType, name);
// 检查是否已存在相同类型的课程
CourseLesson existing = findByType(courseId, lessonType);
if (existing != null) {
log.warn("创建课程环节失败已存在相同类型的课程环节courseId={}, lessonType={}", courseId, lessonType);
throw new BusinessException("该课程包已存在 " + lessonType + " 类型的课程");
}
// 获取最大排序号
Integer maxSortOrder = courseLessonMapper.selectList(
new LambdaQueryWrapper<CourseLesson>()
.eq(CourseLesson::getCourseId, courseId)
).stream()
.map(CourseLesson::getSortOrder)
.max(Integer::compareTo)
.orElse(0);
CourseLesson lesson = new CourseLesson();
lesson.setCourseId(courseId);
lesson.setLessonType(lessonType);
lesson.setName(name);
lesson.setDescription(description);
lesson.setDuration(duration);
lesson.setVideoPath(videoPath);
lesson.setVideoName(videoName);
lesson.setPptPath(pptPath);
lesson.setPptName(pptName);
lesson.setPdfPath(pdfPath);
lesson.setPdfName(pdfName);
lesson.setObjectives(objectives);
lesson.setPreparation(preparation);
lesson.setExtension(extension);
lesson.setReflection(reflection);
// 确保 assessment_data 为有效 JSONMySQL JSON 列不接受纯文本
lesson.setAssessmentData(toValidJsonOrNull(assessmentData));
lesson.setUseTemplate(useTemplate);
lesson.setSortOrder(maxSortOrder + 1);
lesson.setCreatedAt(LocalDateTime.now());
courseLessonMapper.insert(lesson);
log.info("课程环节创建成功id={}, courseId={}, lessonType={}", lesson.getId(), courseId, lessonType);
return lesson;
}
/**
* 更新课程环节
*/
CourseLesson update(Long id, String name, String description, Integer duration,
String videoPath, String videoName, String pptPath, String pptName,
String pdfPath, String pdfName, String objectives, String preparation,
String extension, String reflection, String assessmentData, Boolean useTemplate);
@Transactional(rollbackFor = Exception.class)
public CourseLesson update(Long id, String name, String description, Integer duration,
String videoPath, String videoName, String pptPath, String pptName,
String pdfPath, String pdfName, String objectives, String preparation,
String extension, String reflection, String assessmentData, Boolean useTemplate) {
log.info("更新课程环节id={}", id);
CourseLesson lesson = courseLessonMapper.selectById(id);
if (lesson == null) {
log.warn("课程环节不存在id={}", id);
throw new BusinessException("课程不存在");
}
if (name != null) {
lesson.setName(name);
}
if (description != null) {
lesson.setDescription(description);
}
if (duration != null) {
lesson.setDuration(duration);
}
if (videoPath != null) {
lesson.setVideoPath(videoPath);
}
if (videoName != null) {
lesson.setVideoName(videoName);
}
if (pptPath != null) {
lesson.setPptPath(pptPath);
}
if (pptName != null) {
lesson.setPptName(pptName);
}
if (pdfPath != null) {
lesson.setPdfPath(pdfPath);
}
if (pdfName != null) {
lesson.setPdfName(pdfName);
}
if (objectives != null) {
lesson.setObjectives(objectives);
}
if (preparation != null) {
lesson.setPreparation(preparation);
}
if (extension != null) {
lesson.setExtension(extension);
}
if (reflection != null) {
lesson.setReflection(reflection);
}
if (assessmentData != null) {
lesson.setAssessmentData(toValidJsonOrNull(assessmentData));
}
if (useTemplate != null) {
lesson.setUseTemplate(useTemplate);
}
lesson.setUpdatedAt(LocalDateTime.now());
courseLessonMapper.updateById(lesson);
log.info("课程环节更新成功id={}", id);
return lesson;
}
/**
* 删除课程环节
*/
void delete(Long id);
@Transactional(rollbackFor = Exception.class)
public void delete(Long id) {
log.info("删除课程环节id={}", id);
CourseLesson lesson = courseLessonMapper.selectById(id);
if (lesson == null) {
log.warn("课程环节不存在id={}", id);
throw new BusinessException("课程不存在");
}
courseLessonMapper.deleteById(id);
log.info("课程环节删除成功id={}", id);
}
/**
* 重新排序课程环节
*/
void reorder(Long courseId, List<Long> lessonIds);
@Transactional(rollbackFor = Exception.class)
public void reorder(Long courseId, List<Long> lessonIds) {
log.info("重新排序课程环节courseId={}, count={}", courseId, lessonIds.size());
for (int i = 0; i < lessonIds.size(); i++) {
CourseLesson lesson = courseLessonMapper.selectById(lessonIds.get(i));
if (lesson != null && lesson.getCourseId().equals(courseId)) {
lesson.setSortOrder(i + 1);
courseLessonMapper.updateById(lesson);
}
}
log.info("课程环节重新排序成功courseId={}", courseId);
}
/**
* 查询课程环节的教学环节
*/
List<LessonStep> findSteps(Long lessonId);
public List<LessonStep> findSteps(Long lessonId) {
log.info("查询课程环节的教学环节lessonId={}", lessonId);
List<LessonStep> result = lessonStepMapper.selectList(
new LambdaQueryWrapper<LessonStep>()
.eq(LessonStep::getLessonId, lessonId)
.orderByAsc(LessonStep::getSortOrder)
);
log.info("查询教学环节成功lessonId={}, count={}", lessonId, result.size());
return result;
}
/**
* 创建教学环节
*/
LessonStep createStep(Long lessonId, String name, String content, Integer duration,
String objective, List<Long> resourceIds);
@Transactional(rollbackFor = Exception.class)
public LessonStep createStep(Long lessonId, String name, String content, Integer duration,
String objective, List<Long> resourceIds) {
log.info("创建教学环节lessonId={}, name={}", lessonId, name);
// 获取最大排序号
Integer maxSortOrder = lessonStepMapper.selectList(
new LambdaQueryWrapper<LessonStep>()
.eq(LessonStep::getLessonId, lessonId)
).stream()
.map(LessonStep::getSortOrder)
.max(Integer::compareTo)
.orElse(0);
LessonStep step = new LessonStep();
step.setLessonId(lessonId);
step.setName(name);
step.setContent(content);
step.setDuration(duration != null ? duration : 5);
step.setObjective(objective);
step.setResourceIds(resourceIds != null ? resourceIds.toString() : null);
step.setSortOrder(maxSortOrder + 1);
step.setCreatedAt(LocalDateTime.now());
lessonStepMapper.insert(step);
// 创建环节资源关联
if (resourceIds != null && !resourceIds.isEmpty()) {
for (int i = 0; i < resourceIds.size(); i++) {
LessonStepResource lsr = new LessonStepResource();
lsr.setStepId(step.getId());
lsr.setResourceId(resourceIds.get(i));
lsr.setSortOrder(i);
lessonStepResourceMapper.insert(lsr);
}
}
log.info("教学环节创建成功id={}, lessonId={}", step.getId(), lessonId);
return step;
}
/**
* 更新教学环节
*/
LessonStep updateStep(Long stepId, String name, String content, Integer duration,
String objective, List<Long> resourceIds);
@Transactional(rollbackFor = Exception.class)
public LessonStep updateStep(Long stepId, String name, String content, Integer duration,
String objective, List<Long> resourceIds) {
log.info("更新教学环节stepId={}", stepId);
LessonStep step = lessonStepMapper.selectById(stepId);
if (step == null) {
log.warn("教学环节不存在stepId={}", stepId);
throw new BusinessException("教学环节不存在");
}
if (name != null) {
step.setName(name);
}
if (content != null) {
step.setContent(content);
}
if (duration != null) {
step.setDuration(duration);
}
if (objective != null) {
step.setObjective(objective);
}
if (resourceIds != null) {
step.setResourceIds(resourceIds.toString());
// 删除旧的资源关联
lessonStepResourceMapper.delete(
new LambdaQueryWrapper<LessonStepResource>()
.eq(LessonStepResource::getStepId, stepId)
);
// 创建新的资源关联
for (int i = 0; i < resourceIds.size(); i++) {
LessonStepResource lsr = new LessonStepResource();
lsr.setStepId(stepId);
lsr.setResourceId(resourceIds.get(i));
lsr.setSortOrder(i);
lessonStepResourceMapper.insert(lsr);
}
}
step.setUpdatedAt(LocalDateTime.now());
lessonStepMapper.updateById(step);
log.info("教学环节更新成功stepId={}", stepId);
return step;
}
/**
* 删除教学环节
*/
void deleteStep(Long stepId);
@Transactional(rollbackFor = Exception.class)
public void deleteStep(Long stepId) {
log.info("删除教学环节stepId={}", stepId);
lessonStepMapper.deleteById(stepId);
log.info("教学环节删除成功stepId={}", stepId);
}
/**
* 重新排序教学环节
*/
void reorderSteps(Long lessonId, List<Long> stepIds);
@Transactional(rollbackFor = Exception.class)
public void reorderSteps(Long lessonId, List<Long> stepIds) {
log.info("重新排序教学环节lessonId={}, count={}", lessonId, stepIds.size());
for (int i = 0; i < stepIds.size(); i++) {
LessonStep step = lessonStepMapper.selectById(stepIds.get(i));
if (step != null && step.getLessonId().equals(lessonId)) {
step.setSortOrder(i + 1);
lessonStepMapper.updateById(step);
}
}
log.info("教学环节重新排序成功lessonId={}", lessonId);
}
/**
* assessmentData 转为 MySQL JSON 列可接受的有效 JSON
* 空值返回 null已是有效 JSON 则原样返回否则包装为 JSON 字符串
*/
private String toValidJsonOrNull(String assessmentData) {
if (assessmentData == null || assessmentData.isEmpty()) {
return null;
}
String trimmed = assessmentData.trim();
if (trimmed.isEmpty()) {
return null;
}
// 已是有效 JSON对象或数组则直接使用
if ((trimmed.startsWith("{") && trimmed.endsWith("}")) || (trimmed.startsWith("[") && trimmed.endsWith("]"))) {
try {
JSON.parse(trimmed);
return trimmed;
} catch (Exception ignored) {
// 解析失败当作普通文本处理
}
}
// 普通文本包装为 JSON 字符串
return JSON.toJSONString(assessmentData);
}
/**
* 查询教师的课程环节带权限检查
*/
List<CourseLesson> findCourseLessonsForTeacher(Long courseId, Long tenantId);
public List<CourseLesson> findCourseLessonsForTeacher(Long courseId, Long tenantId) {
log.info("查询教师的课程环节courseId={}, tenantId={}", courseId, tenantId);
// 检查租户是否有权限访问该课程
TenantCourse tenantCourse = tenantCourseMapper.selectOne(
new LambdaQueryWrapper<TenantCourse>()
.eq(TenantCourse::getTenantId, tenantId)
.eq(TenantCourse::getCourseId, courseId)
.eq(TenantCourse::getEnabled, 1)
);
if (tenantCourse == null) {
// 检查是否通过套餐授权
Long count = tenantPackageMapper.selectCount(
new LambdaQueryWrapper<TenantPackage>()
.eq(TenantPackage::getTenantId, tenantId)
.eq(TenantPackage::getStatus, TenantPackageStatus.ACTIVE)
);
if (count == 0) {
log.warn("无权访问该课程courseId={}, tenantId={}", courseId, tenantId);
throw new BusinessException("无权访问该课程");
}
}
List<CourseLesson> result = findByCourseId(courseId);
log.info("查询教师的课程环节成功courseId={}, count={}", courseId, result.size());
return result;
}
}

View File

@ -1,81 +1,271 @@
package com.reading.platform.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.reading.platform.common.exception.BusinessException;
import com.reading.platform.entity.ResourceItem;
import com.reading.platform.entity.ResourceLibrary;
import com.reading.platform.mapper.ResourceItemMapper;
import com.reading.platform.mapper.ResourceLibraryMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 资源库服务接口
* 资源库服务
*/
public interface ResourceLibraryService extends IService<ResourceLibrary> {
@Slf4j
@Service
@RequiredArgsConstructor
public class ResourceLibraryService extends ServiceImpl<ResourceLibraryMapper, ResourceLibrary> {
private final ResourceLibraryMapper libraryMapper;
private final ResourceItemMapper itemMapper;
/**
* 分页查询资源库
*/
Page<ResourceLibrary> findAllLibraries(String libraryType, String keyword, Integer page, Integer pageSize);
public Page<ResourceLibrary> findAllLibraries(String libraryType, String keyword, Integer page, Integer pageSize) {
log.info("查询资源库列表libraryType={}, keyword={}, page={}, pageSize={}", libraryType, keyword, page, pageSize);
Page<ResourceLibrary> pageParam = new Page<>(page, pageSize);
LambdaQueryWrapper<ResourceLibrary> wrapper = new LambdaQueryWrapper<>();
if (libraryType != null && !libraryType.isEmpty()) {
wrapper.eq(ResourceLibrary::getLibraryType, libraryType);
}
if (keyword != null && !keyword.isEmpty()) {
wrapper.like(ResourceLibrary::getName, keyword);
}
wrapper.orderByDesc(ResourceLibrary::getCreatedAt);
Page<ResourceLibrary> result = libraryMapper.selectPage(pageParam, wrapper);
log.info("查询资源库列表成功,总数={}", result.getTotal());
return result;
}
/**
* 查询资源库详情
*/
ResourceLibrary findLibraryById(String id);
public ResourceLibrary findLibraryById(String id) {
log.info("查询资源库详情id={}", id);
ResourceLibrary library = libraryMapper.selectById(id);
if (library == null) {
log.warn("资源库不存在id={}", id);
throw new BusinessException("资源库不存在");
}
return library;
}
/**
* 查询资源库不存在时返回 null
*/
ResourceLibrary findLibraryByIdOrNull(String id);
public ResourceLibrary findLibraryByIdOrNull(String id) {
if (id == null || id.isEmpty()) return null;
return libraryMapper.selectById(id);
}
/**
* 创建资源库
*/
ResourceLibrary createLibrary(String name, String type, String description, String tenantId);
public ResourceLibrary createLibrary(String name, String type, String description, String tenantId) {
log.info("创建资源库name={}, type={}, tenantId={}", name, type, tenantId);
ResourceLibrary library = new ResourceLibrary();
library.setName(name);
library.setLibraryType(type);
library.setDescription(description);
library.setTenantId(tenantId);
library.setStatus("ACTIVE");
library.setSortOrder(0);
libraryMapper.insert(library);
log.info("资源库创建成功id={}", library.getId());
return library;
}
/**
* 更新资源库
*/
ResourceLibrary updateLibrary(String id, String name, String description);
public ResourceLibrary updateLibrary(String id, String name, String description) {
log.info("更新资源库id={}, name={}, description={}", id, name, description);
ResourceLibrary library = libraryMapper.selectById(id);
if (library == null) {
log.warn("资源库不存在id={}", id);
throw new BusinessException("资源库不存在");
}
if (name != null) {
library.setName(name);
}
if (description != null) {
library.setDescription(description);
}
libraryMapper.updateById(library);
log.info("资源库更新成功id={}", id);
return library;
}
/**
* 删除资源库
*/
void deleteLibrary(String id);
public void deleteLibrary(String id) {
log.info("删除资源库id={}", id);
// 先删除该资源库下的所有资源项
LambdaQueryWrapper<ResourceItem> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(ResourceItem::getLibraryId, id);
itemMapper.delete(wrapper);
libraryMapper.deleteById(id);
log.info("资源库删除成功id={}", id);
}
/**
* 分页查询资源项目
*/
Page<ResourceItem> findAllItems(String libraryId, String fileType, String keyword, Integer page, Integer pageSize);
public Page<ResourceItem> findAllItems(String libraryId, String fileType, String keyword, Integer page, Integer pageSize) {
log.info("查询资源项目列表libraryId={}, fileType={}, keyword={}, page={}, pageSize={}", libraryId, fileType, keyword, page, pageSize);
Page<ResourceItem> pageParam = new Page<>(page, pageSize);
LambdaQueryWrapper<ResourceItem> wrapper = new LambdaQueryWrapper<>();
if (libraryId != null && !libraryId.isEmpty()) {
wrapper.eq(ResourceItem::getLibraryId, libraryId);
}
if (fileType != null && !fileType.isEmpty()) {
wrapper.eq(ResourceItem::getFileType, fileType);
}
if (keyword != null && !keyword.isEmpty()) {
wrapper.and(w -> w.like(ResourceItem::getTitle, keyword)
.or()
.like(ResourceItem::getDescription, keyword));
}
wrapper.orderByDesc(ResourceItem::getCreatedAt);
Page<ResourceItem> result = itemMapper.selectPage(pageParam, wrapper);
log.info("查询资源项目列表成功,总数={}", result.getTotal());
return result;
}
/**
* 查询资源项目详情
*/
ResourceItem findItemById(String id);
public ResourceItem findItemById(String id) {
log.info("查询资源项目详情id={}", id);
ResourceItem item = itemMapper.selectById(id);
if (item == null) {
log.warn("资源项目不存在id={}", id);
throw new BusinessException("资源项目不存在");
}
return item;
}
/**
* 创建资源项目数字资源
*/
ResourceItem createItem(String libraryId, String title, String fileType, String filePath,
Long fileSize, String description, String tags, String tenantId);
public ResourceItem createItem(String libraryId, String title, String fileType, String filePath,
Long fileSize, String description, String tags, String tenantId) {
log.info("创建资源项目libraryId={}, title={}, fileType={}, fileSize={}, tenantId={}", libraryId, title, fileType, fileSize, tenantId);
ResourceItem item = new ResourceItem();
item.setId(null); // 确保由数据库 AUTO_INCREMENT 生成避免主键冲突
item.setLibraryId(libraryId); // 目标资源库 ID
item.setTitle(title);
item.setName(title); // 数据库 name 字段 NOT NULL title 保持一致
item.setDescription(description);
item.setFileType(fileType);
item.setFilePath(filePath);
item.setFileSize(fileSize);
item.setTags(tags);
item.setSortOrder(0);
item.setTenantId(tenantId);
item.setStatus("ACTIVE");
itemMapper.insert(item);
log.info("资源项目创建成功id={}", item.getId());
return item;
}
/**
* 更新资源项目数字资源
*/
ResourceItem updateItem(String id, String title, String description, String tags);
public ResourceItem updateItem(String id, String title, String description, String tags) {
log.info("更新资源项目id={}, title={}, description={}, tags={}", id, title, description, tags);
ResourceItem item = itemMapper.selectById(id);
if (item == null) {
log.warn("资源项目不存在id={}", id);
throw new BusinessException("资源项目不存在");
}
if (title != null) {
item.setTitle(title);
}
if (description != null) {
item.setDescription(description);
}
if (tags != null) {
item.setTags(tags);
}
itemMapper.updateById(item);
log.info("资源项目更新成功id={}", id);
return item;
}
/**
* 删除资源项目
*/
void deleteItem(String id);
public void deleteItem(String id) {
log.info("删除资源项目id={}", id);
itemMapper.deleteById(id);
log.info("资源项目删除成功id={}", id);
}
/**
* 批量删除资源项目
*/
void batchDeleteItems(List<String> ids);
public void batchDeleteItems(List<String> ids) {
log.info("批量删除资源项目ids={}", ids);
itemMapper.deleteBatchIds(ids);
log.info("批量删除资源项目成功,数量={}", ids.size());
}
/**
* 获取统计数据
*/
Map<String, Object> getStats();
public Map<String, Object> getStats() {
log.info("获取资源库统计数据");
Map<String, Object> stats = new HashMap<>();
Long libraryCount = libraryMapper.selectCount(null);
Long itemCount = itemMapper.selectCount(null);
stats.put("totalLibraries", libraryCount);
stats.put("totalItems", itemCount);
// 按资源库类型统计资源数量绘本资源教学材料等
Map<String, Long> itemsByLibraryType = new HashMap<>();
LambdaQueryWrapper<ResourceLibrary> libWrapper = new LambdaQueryWrapper<>();
for (ResourceLibrary lib : libraryMapper.selectList(libWrapper)) {
String type = lib.getLibraryType();
if (type != null) {
LambdaQueryWrapper<ResourceItem> itemWrapper = new LambdaQueryWrapper<>();
itemWrapper.eq(ResourceItem::getLibraryId, String.valueOf(lib.getId()));
long count = itemMapper.selectCount(itemWrapper);
itemsByLibraryType.merge(type, count, Long::sum);
}
}
stats.put("itemsByLibraryType", itemsByLibraryType);
log.info("资源库统计数据totalLibraries={}, totalItems={}, itemsByLibraryType={}", libraryCount, itemCount, itemsByLibraryType);
return stats;
}
}

View File

@ -1,42 +1,142 @@
package com.reading.platform.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.reading.platform.common.exception.BusinessException;
import com.reading.platform.entity.CoursePackage;
import com.reading.platform.entity.Theme;
import com.reading.platform.mapper.CoursePackageMapper;
import com.reading.platform.mapper.ThemeMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* 主题字典服务接口
* 主题字典服务
*/
public interface ThemeService extends IService<Theme> {
@Slf4j
@Service
@RequiredArgsConstructor
public class ThemeService extends ServiceImpl<ThemeMapper, Theme> {
private final ThemeMapper themeMapper;
private final CoursePackageMapper courseMapper;
/**
* 查询所有主题
*/
List<Theme> findAll();
public List<Theme> findAll() {
log.info("查询所有主题");
List<Theme> result = lambdaQuery()
.orderByAsc(Theme::getSortOrder)
.list();
log.info("查询所有主题成功count={}", result.size());
return result;
}
/**
* 查询主题详情
*/
Theme findById(Long id);
public Theme findById(Long id) {
log.info("查询主题详情id={}", id);
Theme theme = themeMapper.selectById(id);
if (theme == null) {
log.warn("主题不存在id={}", id);
throw new BusinessException("主题不存在");
}
return theme;
}
/**
* 创建主题
*/
Theme create(String name, String description, Integer sortOrder);
@Transactional(rollbackFor = Exception.class)
public Theme create(String name, String description, Integer sortOrder) {
log.info("创建主题name={}, sortOrder={}", name, sortOrder);
// 获取最大排序号
Integer maxSortOrder = themeMapper.selectList(null)
.stream()
.map(Theme::getSortOrder)
.max(Integer::compareTo)
.orElse(0);
Theme theme = new Theme();
theme.setName(name);
theme.setDescription(description);
theme.setSortOrder(sortOrder != null ? sortOrder : maxSortOrder + 1);
theme.setStatus("ACTIVE");
themeMapper.insert(theme);
log.info("主题创建成功id={}", theme.getId());
return theme;
}
/**
* 更新主题
*/
Theme update(Long id, String name, String description, Integer sortOrder, String status);
@Transactional(rollbackFor = Exception.class)
public Theme update(Long id, String name, String description, Integer sortOrder, String status) {
log.info("更新主题id={}, name={}, status={}", id, name, status);
Theme theme = themeMapper.selectById(id);
if (theme == null) {
log.warn("主题不存在id={}", id);
throw new BusinessException("主题不存在");
}
if (name != null) {
theme.setName(name);
}
if (description != null) {
theme.setDescription(description);
}
if (sortOrder != null) {
theme.setSortOrder(sortOrder);
}
if (status != null) {
theme.setStatus(status);
}
themeMapper.updateById(theme);
log.info("主题更新成功id={}", id);
return theme;
}
/**
* 删除主题
*/
void delete(Long id);
@Transactional(rollbackFor = Exception.class)
public void delete(Long id) {
log.info("删除主题id={}", id);
// 检查是否有关联课程
Long courseCount = courseMapper.selectCount(
new LambdaQueryWrapper<CoursePackage>().eq(CoursePackage::getThemeId, id)
);
if (courseCount > 0) {
log.warn("删除主题失败,该主题下有 {} 个课程包id={}", courseCount, id);
throw new BusinessException("该主题下有 " + courseCount + " 个课程包,无法删除");
}
themeMapper.deleteById(id);
log.info("主题删除成功id={}", id);
}
/**
* 重新排序
*/
void reorder(List<Long> ids);
@Transactional(rollbackFor = Exception.class)
public void reorder(List<Long> ids) {
log.info("重新排序主题count={}", ids.size());
for (int i = 0; i < ids.size(); i++) {
Theme theme = themeMapper.selectById(ids.get(i));
if (theme != null) {
theme.setSortOrder(i + 1);
themeMapper.updateById(theme);
}
}
log.info("主题重新排序成功");
}
}

View File

@ -1,633 +0,0 @@
package com.reading.platform.service.impl;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.reading.platform.common.enums.CourseStatus;
import com.reading.platform.common.enums.TenantPackageStatus;
import com.reading.platform.common.exception.BusinessException;
import com.reading.platform.common.response.PageResult;
import com.reading.platform.dto.response.CourseCollectionResponse;
import com.reading.platform.dto.response.CoursePackageResponse;
import com.reading.platform.entity.*;
import com.reading.platform.mapper.*;
import com.reading.platform.service.CourseCollectionService;
import com.reading.platform.service.CourseLessonService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* 课程套餐服务实现类两层结构 - 最上层
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class CourseCollectionServiceImpl extends ServiceImpl<CourseCollectionMapper, CourseCollection> implements CourseCollectionService {
private final CourseCollectionMapper collectionMapper;
private final CourseCollectionPackageMapper collectionPackageMapper;
private final CoursePackageMapper packageMapper;
private final TenantPackageMapper tenantPackageMapper;
private final CoursePackageCourseMapper packageCoursePackageMapper;
private final CoursePackageMapper courseMapper;
private final CourseLessonService courseLessonService;
/**
* 查询租户的课程套餐列表
*/
@Override
public List<CourseCollectionResponse> findTenantCollections(Long tenantId) {
log.info("查询租户课程套餐tenantId={}", tenantId);
// 查询租户的课程套餐关联
List<TenantPackage> tenantPackages = tenantPackageMapper.selectList(
new LambdaQueryWrapper<TenantPackage>()
.eq(TenantPackage::getTenantId, tenantId)
.eq(TenantPackage::getStatus, TenantPackageStatus.ACTIVE.getCode())
.isNotNull(TenantPackage::getCollectionId)
.orderByDesc(TenantPackage::getCreatedAt)
);
log.info("查询到{}条租户套餐关联记录", tenantPackages.size());
for (TenantPackage tp : tenantPackages) {
log.info(" - collection_id: {}, start_date: {}, end_date: {}",
tp.getCollectionId(), tp.getStartDate(), tp.getEndDate());
}
// 获取套餐详情并转换为响应对象
List<CourseCollectionResponse> result = tenantPackages.stream()
.map(tp -> {
CourseCollection collection = collectionMapper.selectById(tp.getCollectionId());
if (collection == null) {
log.warn("套餐不存在collection_id={}", tp.getCollectionId());
return null;
}
// 过滤下架状态的课程套餐
if (CourseStatus.ARCHIVED.getCode().equals(collection.getStatus())) {
log.warn("套餐已下架collection_id={}", tp.getCollectionId());
return null;
}
log.info("返回套餐id={}, name={}, package_count={}",
collection.getId(), collection.getName(), collection.getPackageCount());
CourseCollectionResponse response = toResponse(collection);
// 设置租户套餐的额外信息如果有
response.setStartDate(tp.getStartDate());
response.setEndDate(tp.getEndDate());
return response;
})
.filter(r -> r != null)
.collect(Collectors.toList());
log.info("查询到{}个课程套餐", result.size());
return result;
}
/**
* 获取课程套餐详情包含课程包列表
*/
@Override
public CourseCollectionResponse getCollectionDetail(Long collectionId) {
log.info("获取课程套餐详情collectionId={}", collectionId);
CourseCollection collection = collectionMapper.selectById(collectionId);
if (collection == null) {
log.warn("课程套餐不存在collectionId={}", collectionId);
return null;
}
return toResponse(collection);
}
/**
* 分页查询课程套餐
*/
@Override
public Page<CourseCollectionResponse> pageCollections(Integer pageNum, Integer pageSize, String status) {
log.info("分页查询课程套餐pageNum={}, pageSize={}, status={}", pageNum, pageSize, status);
LambdaQueryWrapper<CourseCollection> wrapper = new LambdaQueryWrapper<>();
if (StringUtils.hasText(status)) {
wrapper.eq(CourseCollection::getStatus, status);
}
wrapper.orderByDesc(CourseCollection::getCreatedAt);
Page<CourseCollection> page = collectionMapper.selectPage(
new Page<>(pageNum, pageSize),
wrapper
);
// 转换为响应对象
List<CourseCollectionResponse> responses = page.getRecords().stream()
.map(this::toResponse)
.collect(Collectors.toList());
Page<CourseCollectionResponse> result = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
result.setRecords(responses);
return result;
}
/**
* 获取课程套餐下的课程包列表
*/
@Override
public List<CoursePackageResponse> getPackagesByCollection(Long collectionId) {
log.info("获取课程套餐的课程包列表collectionId={}", collectionId);
// 查询关联关系
List<CourseCollectionPackage> associations = collectionPackageMapper.selectList(
new LambdaQueryWrapper<CourseCollectionPackage>()
.eq(CourseCollectionPackage::getCollectionId, collectionId)
.orderByAsc(CourseCollectionPackage::getSortOrder)
);
if (associations.isEmpty()) {
return new ArrayList<>();
}
// 获取课程包详情
List<Long> packageIds = associations.stream()
.map(CourseCollectionPackage::getPackageId)
.collect(Collectors.toList());
List<CoursePackage> packages = packageMapper.selectList(
new LambdaQueryWrapper<CoursePackage>()
.in(CoursePackage::getId, packageIds)
.eq(CoursePackage::getStatus, CourseStatus.PUBLISHED.getCode())
);
// 转换为响应对象并设置排序号
List<CoursePackageResponse> result = packages.stream()
.map(pkg -> {
CoursePackageResponse response = toPackageResponse(pkg);
// 设置排序号
associations.stream()
.filter(a -> a.getPackageId().equals(pkg.getId()))
.findFirst()
.ifPresent(a -> response.setSortOrder(a.getSortOrder()));
return response;
})
.collect(Collectors.toList());
log.info("查询到{}个课程包", result.size());
return result;
}
/**
* 创建课程套餐
*/
@Override
@Transactional(rollbackFor = Exception.class)
public CourseCollectionResponse createCollection(String name, String description, Long price,
Long discountPrice, String discountType, String[] gradeLevels) {
log.info("创建课程套餐name={}", name);
CourseCollection collection = new CourseCollection();
collection.setName(name);
collection.setDescription(description);
collection.setPrice(price);
collection.setDiscountPrice(discountPrice);
collection.setDiscountType(discountType);
// 将数组转为 JSON 字符串存储到 JSON 字段
collection.setGradeLevels(JSON.toJSONString(gradeLevels));
collection.setPackageCount(0);
collection.setStatus(CourseStatus.DRAFT.getCode());
collectionMapper.insert(collection);
log.info("课程套餐创建成功id={}", collection.getId());
return toResponse(collection);
}
/**
* 设置课程套餐的课程包
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void setCollectionPackages(Long collectionId, List<Long> packageIds) {
log.info("设置课程套餐的课程包collectionId={}, packageCount={}", collectionId, packageIds.size());
// 验证课程套餐是否存在
CourseCollection collection = collectionMapper.selectById(collectionId);
if (collection == null) {
throw new BusinessException("课程套餐不存在");
}
// 去重保持首次出现顺序避免 uk_collection_package 唯一约束冲突
List<Long> distinctIds = packageIds.stream().distinct().collect(Collectors.toList());
// 验证课程包是否存在应用层外键约束
if (!distinctIds.isEmpty()) {
List<CoursePackage> packages = packageMapper.selectList(
new LambdaQueryWrapper<CoursePackage>()
.in(CoursePackage::getId, distinctIds)
);
if (packages.size() != distinctIds.size()) {
throw new BusinessException("存在无效的课程包 ID");
}
}
// 删除旧的关联
collectionPackageMapper.delete(
new LambdaQueryWrapper<CourseCollectionPackage>()
.eq(CourseCollectionPackage::getCollectionId, collectionId)
);
// 创建新的关联在事务内顺序插入
for (int i = 0; i < distinctIds.size(); i++) {
CourseCollectionPackage association = new CourseCollectionPackage();
association.setCollectionId(collectionId);
association.setPackageId(distinctIds.get(i));
association.setSortOrder(i + 1);
collectionPackageMapper.insert(association);
}
// 更新课程包数量复用前面已验证的 collection 变量
collection.setPackageCount(distinctIds.size());
collectionMapper.updateById(collection);
log.info("课程套餐的课程包设置完成");
}
/**
* 更新课程套餐
*/
@Override
@Transactional(rollbackFor = Exception.class)
public CourseCollectionResponse updateCollection(Long id, String name, String description, Long price,
Long discountPrice, String discountType, String[] gradeLevels) {
log.info("更新课程套餐id={}", id);
CourseCollection collection = collectionMapper.selectById(id);
if (collection == null) {
throw new IllegalArgumentException("课程套餐不存在");
}
collection.setName(name);
collection.setDescription(description);
collection.setPrice(price);
collection.setDiscountPrice(discountPrice);
collection.setDiscountType(discountType);
// 将数组转为 JSON 字符串存储到 JSON 字段
collection.setGradeLevels(JSON.toJSONString(gradeLevels));
collectionMapper.updateById(collection);
log.info("课程套餐更新成功id={}", id);
return toResponse(collection);
}
/**
* 删除课程套餐
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteCollection(Long id) {
log.info("删除课程套餐id={}", id);
// 检查是否有租户正在使用此课程套餐
Long tenantCount = tenantPackageMapper.selectCount(
new LambdaQueryWrapper<TenantPackage>()
.eq(TenantPackage::getCollectionId, id)
.eq(TenantPackage::getStatus, TenantPackageStatus.ACTIVE)
);
if (tenantCount > 0) {
log.warn("删除课程套餐失败,有 {} 个租户正在使用此套餐id={}", tenantCount, id);
throw new BusinessException("" + tenantCount + " 个租户正在使用此课程套餐,无法删除");
}
// 清理所有状态的租户套餐关联记录包括非活跃状态
List<TenantPackage> allTenantPackages = tenantPackageMapper.selectList(
new LambdaQueryWrapper<TenantPackage>()
.eq(TenantPackage::getCollectionId, id)
);
if (!allTenantPackages.isEmpty()) {
tenantPackageMapper.delete(
new LambdaQueryWrapper<TenantPackage>()
.eq(TenantPackage::getCollectionId, id)
);
log.info("已清理 {} 条租户套餐关联记录", allTenantPackages.size());
}
// 删除课程套餐与课程包的关联关系
List<CourseCollectionPackage> collectionPackages = collectionPackageMapper.selectList(
new LambdaQueryWrapper<CourseCollectionPackage>()
.eq(CourseCollectionPackage::getCollectionId, id)
);
if (!collectionPackages.isEmpty()) {
collectionPackageMapper.delete(
new LambdaQueryWrapper<CourseCollectionPackage>()
.eq(CourseCollectionPackage::getCollectionId, id)
);
log.info("已清理 {} 条课程包关联记录", collectionPackages.size());
}
// 删除课程套餐
collectionMapper.deleteById(id);
log.info("课程套餐删除成功id={}", id);
}
/**
* 发布课程套餐
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void publishCollection(Long id) {
log.info("发布课程套餐id={}", id);
CourseCollection collection = collectionMapper.selectById(id);
if (collection == null) {
throw new IllegalArgumentException("课程套餐不存在");
}
collection.setStatus(CourseStatus.PUBLISHED.getCode());
collection.setPublishedAt(LocalDateTime.now());
collectionMapper.updateById(collection);
log.info("课程套餐发布成功id={}", id);
}
/**
* 下架课程套餐
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void archiveCollection(Long id) {
log.info("下架课程套餐id={}", id);
CourseCollection collection = collectionMapper.selectById(id);
if (collection == null) {
throw new IllegalArgumentException("课程套餐不存在");
}
collection.setStatus(CourseStatus.ARCHIVED.getCode());
collectionMapper.updateById(collection);
log.info("课程套餐下架成功id={}", id);
}
/**
* 重新发布课程套餐
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void republishCollection(Long id) {
log.info("重新发布课程套餐id={}", id);
CourseCollection collection = collectionMapper.selectById(id);
if (collection == null) {
throw new IllegalArgumentException("课程套餐不存在");
}
collection.setStatus(CourseStatus.PUBLISHED.getCode());
collection.setPublishedAt(LocalDateTime.now());
collectionMapper.updateById(collection);
log.info("课程套餐重新发布成功id={}", id);
}
/**
* 撤销审核
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void withdrawCollection(Long id) {
log.info("撤销课程套餐审核id={}", id);
CourseCollection collection = collectionMapper.selectById(id);
if (collection == null) {
throw new IllegalArgumentException("课程套餐不存在");
}
collection.setStatus(CourseStatus.DRAFT.getCode());
collectionMapper.updateById(collection);
log.info("课程套餐审核撤销成功id={}", id);
}
/**
* 提交课程套餐审核
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void submitCollection(Long id) {
log.info("提交课程套餐审核id={}", id);
CourseCollection collection = collectionMapper.selectById(id);
if (collection == null) {
throw new IllegalArgumentException("课程套餐不存在");
}
// 检查是否包含课程包
if (collection.getPackageCount() == null || collection.getPackageCount() == 0) {
throw new BusinessException("课程套餐必须包含至少一个课程包");
}
collection.setStatus(CourseStatus.PENDING.getCode());
collection.setSubmittedAt(LocalDateTime.now());
collectionMapper.updateById(collection);
log.info("课程套餐提交审核成功id={}", id);
}
/**
* 审核驳回课程套餐
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void rejectCollection(Long id, String comment) {
log.info("审核驳回课程套餐id={}, comment={}", id, comment);
CourseCollection collection = collectionMapper.selectById(id);
if (collection == null) {
throw new IllegalArgumentException("课程套餐不存在");
}
collection.setStatus(CourseStatus.REJECTED.getCode());
collection.setReviewedAt(LocalDateTime.now());
collection.setReviewComment(comment);
collectionMapper.updateById(collection);
log.info("课程套餐审核驳回成功id={}", id);
}
/**
* 续费租户课程套餐
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void renewTenantCollection(Long tenantId, Long collectionId, java.time.LocalDate endDate, Long pricePaid) {
log.info("续费租户课程套餐tenantId={}, collectionId={}, endDate={}, pricePaid={}", tenantId, collectionId, endDate, pricePaid);
// 查询现有租户套餐关联
List<TenantPackage> existingPackages = tenantPackageMapper.selectList(
new LambdaQueryWrapper<TenantPackage>()
.eq(TenantPackage::getTenantId, tenantId)
.eq(TenantPackage::getCollectionId, collectionId)
);
if (!existingPackages.isEmpty()) {
// 更新现有记录
TenantPackage existing = existingPackages.get(0);
existing.setEndDate(endDate);
existing.setStatus(TenantPackageStatus.ACTIVE);
if (pricePaid != null) {
existing.setPricePaid(pricePaid);
}
existing.setUpdatedAt(LocalDateTime.now());
tenantPackageMapper.updateById(existing);
log.info("租户课程套餐续费成功tenantId={}, collectionId={}", tenantId, collectionId);
} else {
// 查询课程套餐信息
CourseCollection collection = collectionMapper.selectById(collectionId);
if (collection == null) {
throw new IllegalArgumentException("课程套餐不存在");
}
// 创建新记录
TenantPackage tp = new TenantPackage();
tp.setTenantId(tenantId);
tp.setCollectionId(collectionId);
tp.setStartDate(java.time.LocalDate.now());
tp.setEndDate(endDate);
tp.setStatus(TenantPackageStatus.ACTIVE);
tp.setPricePaid(pricePaid != null ? pricePaid :
(collection.getDiscountPrice() != null ? collection.getDiscountPrice() : collection.getPrice()));
tp.setCreatedAt(LocalDateTime.now());
tenantPackageMapper.insert(tp);
log.info("租户课程套餐新办成功tenantId={}, collectionId={}", tenantId, collectionId);
}
}
/**
* 解析适用年级数据库存储为 JSON 数组 ["小班","中班","大班"]需正确解析
*/
private String[] parseGradeLevels(String gradeLevels) {
if (!StringUtils.hasText(gradeLevels)) {
return new String[0];
}
try {
if (gradeLevels.trim().startsWith("[")) {
return JSON.parseArray(gradeLevels, String.class).toArray(new String[0]);
}
// 兼容旧数据逗号分隔格式
return gradeLevels.split(",");
} catch (Exception e) {
log.warn("解析 gradeLevels 失败:{}", gradeLevels, e);
return new String[0];
}
}
/**
* 转换为响应对象
*/
private CourseCollectionResponse toResponse(CourseCollection collection) {
// 获取课程包列表
List<CourseCollectionResponse.CoursePackageItem> packages = getPackagesByCollection(collection.getId()).stream()
.map(pkg -> {
CourseCollectionResponse.CoursePackageItem item = new CourseCollectionResponse.CoursePackageItem();
item.setId(pkg.getId());
item.setName(pkg.getName());
item.setDescription(pkg.getDescription());
item.setGradeLevels(pkg.getGradeLevels() != null ? pkg.getGradeLevels() : new String[0]);
item.setCourseCount(pkg.getCourseCount() != null ? pkg.getCourseCount() : 0);
item.setSortOrder(pkg.getSortOrder());
return item;
})
.collect(Collectors.toList());
return CourseCollectionResponse.builder()
.id(collection.getId())
.name(collection.getName())
.description(collection.getDescription())
.price(collection.getPrice())
.discountPrice(collection.getDiscountPrice())
.discountType(collection.getDiscountType())
.gradeLevels(parseGradeLevels(collection.getGradeLevels()))
.packageCount(collection.getPackageCount())
.status(collection.getStatus())
.submittedAt(collection.getSubmittedAt())
.submittedBy(collection.getSubmittedBy())
.reviewedAt(collection.getReviewedAt())
.reviewedBy(collection.getReviewedBy())
.reviewComment(collection.getReviewComment())
.publishedAt(collection.getPublishedAt())
.createdAt(collection.getCreatedAt())
.updatedAt(collection.getUpdatedAt())
.packages(packages)
.build();
}
/**
* 转换为课程包响应对象包含课程列表和排课计划参考
*/
private CoursePackageResponse toPackageResponse(CoursePackage pkg) {
// 解析 gradeTagsCoursePackage 使用 gradeTags
String[] gradeLevelsArray = new String[0];
if (pkg.getGradeTags() != null && !pkg.getGradeTags().isEmpty()) {
try {
// gradeTags JSON 数组格式尝试解析
if (pkg.getGradeTags().startsWith("[")) {
gradeLevelsArray = com.alibaba.fastjson2.JSON.parseArray(pkg.getGradeTags(), String.class).toArray(new String[0]);
} else {
gradeLevelsArray = pkg.getGradeTags().split(",");
}
} catch (Exception e) {
gradeLevelsArray = new String[0];
}
}
List<CoursePackageResponse.CoursePackageCourseItem> courseItems = new ArrayList<>();
// 查询课程包关联的课程环节
List<CourseLesson> lessons = courseLessonService.findByCourseId(pkg.getId());
if (!lessons.isEmpty()) {
courseItems = lessons.stream()
.map(lesson -> {
CoursePackageResponse.CoursePackageCourseItem item = new CoursePackageResponse.CoursePackageCourseItem();
item.setId(lesson.getId());
item.setName(lesson.getName());
item.setGradeLevel(null); // 课程环节没有年级字段
item.setSortOrder(lesson.getSortOrder());
item.setScheduleRefData(pkg.getScheduleRefData());
item.setLessonType(lesson.getLessonType());
return item;
})
.collect(Collectors.toList());
}
return CoursePackageResponse.builder()
.id(pkg.getId())
.name(pkg.getName())
.description(pkg.getDescription())
.price(null) // CoursePackage 没有价格字段
.discountPrice(null)
.discountType(null)
.gradeLevels(gradeLevelsArray)
.courseCount(lessons.size()) // 使用课程环节数量
.status(pkg.getStatus())
.submittedAt(pkg.getSubmittedAt())
.submittedBy(pkg.getSubmittedBy())
.reviewedAt(pkg.getReviewedAt())
.reviewedBy(pkg.getReviewedBy())
.reviewComment(pkg.getReviewComment())
.publishedAt(pkg.getPublishedAt())
.createdAt(pkg.getCreatedAt())
.updatedAt(pkg.getUpdatedAt())
.courses(courseItems)
.build();
}
}

View File

@ -1,430 +0,0 @@
package com.reading.platform.service.impl;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.reading.platform.common.enums.TenantPackageStatus;
import com.reading.platform.common.exception.BusinessException;
import com.reading.platform.entity.*;
import com.reading.platform.mapper.*;
import com.reading.platform.service.CourseLessonService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List;
/**
* 课程环节服务实现类
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class CourseLessonServiceImpl extends ServiceImpl<CourseLessonMapper, CourseLesson> implements CourseLessonService {
private final CourseLessonMapper courseLessonMapper;
private final LessonStepMapper lessonStepMapper;
private final LessonStepResourceMapper lessonStepResourceMapper;
private final TenantCourseMapper tenantCourseMapper;
private final TenantPackageMapper tenantPackageMapper;
/**
* 查询课程的所有环节
*/
@Override
public List<CourseLesson> findByCourseId(Long courseId) {
log.info("查询课程的所有环节courseId={}", courseId);
List<CourseLesson> result = courseLessonMapper.selectList(
new LambdaQueryWrapper<CourseLesson>()
.eq(CourseLesson::getCourseId, courseId)
.orderByAsc(CourseLesson::getSortOrder)
);
log.info("查询课程环节成功courseId={}, count={}", courseId, result.size());
return result;
}
/**
* 查询课程环节详情
*/
@Override
public CourseLesson findById(Long id) {
log.info("查询课程环节详情id={}", id);
CourseLesson lesson = courseLessonMapper.selectById(id);
if (lesson == null) {
log.warn("课程环节不存在id={}", id);
throw new BusinessException("课程环节不存在");
}
return lesson;
}
/**
* 按类型查询课程环节
*/
@Override
public CourseLesson findByType(Long courseId, String lessonType) {
log.info("按类型查询课程环节courseId={}, lessonType={}", courseId, lessonType);
CourseLesson result = courseLessonMapper.selectOne(
new LambdaQueryWrapper<CourseLesson>()
.eq(CourseLesson::getCourseId, courseId)
.eq(CourseLesson::getLessonType, lessonType)
);
if (result != null) {
log.info("查询课程环节成功courseId={}, lessonType={}, id={}", courseId, lessonType, result.getId());
}
return result;
}
/**
* 创建课程环节
*/
@Override
@Transactional(rollbackFor = Exception.class)
public CourseLesson create(Long courseId, String lessonType, String name, String description,
Integer duration, String videoPath, String videoName,
String pptPath, String pptName, String pdfPath, String pdfName,
String objectives, String preparation, String extension,
String reflection, String assessmentData, Boolean useTemplate) {
log.info("创建课程环节courseId={}, lessonType={}, name={}", courseId, lessonType, name);
// 检查是否已存在相同类型的课程
CourseLesson existing = findByType(courseId, lessonType);
if (existing != null) {
log.warn("创建课程环节失败已存在相同类型的课程环节courseId={}, lessonType={}", courseId, lessonType);
throw new BusinessException("该课程包已存在 " + lessonType + " 类型的课程");
}
// 获取最大排序号
Integer maxSortOrder = courseLessonMapper.selectList(
new LambdaQueryWrapper<CourseLesson>()
.eq(CourseLesson::getCourseId, courseId)
).stream()
.map(CourseLesson::getSortOrder)
.max(Integer::compareTo)
.orElse(0);
CourseLesson lesson = new CourseLesson();
lesson.setCourseId(courseId);
lesson.setLessonType(lessonType);
lesson.setName(name);
lesson.setDescription(description);
lesson.setDuration(duration);
lesson.setVideoPath(videoPath);
lesson.setVideoName(videoName);
lesson.setPptPath(pptPath);
lesson.setPptName(pptName);
lesson.setPdfPath(pdfPath);
lesson.setPdfName(pdfName);
lesson.setObjectives(objectives);
lesson.setPreparation(preparation);
lesson.setExtension(extension);
lesson.setReflection(reflection);
// 确保 assessment_data 为有效 JSONMySQL JSON 列不接受纯文本
lesson.setAssessmentData(toValidJsonOrNull(assessmentData));
lesson.setUseTemplate(useTemplate);
lesson.setSortOrder(maxSortOrder + 1);
lesson.setCreatedAt(LocalDateTime.now());
courseLessonMapper.insert(lesson);
log.info("课程环节创建成功id={}, courseId={}, lessonType={}", lesson.getId(), courseId, lessonType);
return lesson;
}
/**
* 更新课程环节
*/
@Override
@Transactional(rollbackFor = Exception.class)
public CourseLesson update(Long id, String name, String description, Integer duration,
String videoPath, String videoName, String pptPath, String pptName,
String pdfPath, String pdfName, String objectives, String preparation,
String extension, String reflection, String assessmentData, Boolean useTemplate) {
log.info("更新课程环节id={}", id);
CourseLesson lesson = courseLessonMapper.selectById(id);
if (lesson == null) {
log.warn("课程环节不存在id={}", id);
throw new BusinessException("课程不存在");
}
if (name != null) {
lesson.setName(name);
}
if (description != null) {
lesson.setDescription(description);
}
if (duration != null) {
lesson.setDuration(duration);
}
if (videoPath != null) {
lesson.setVideoPath(videoPath);
}
if (videoName != null) {
lesson.setVideoName(videoName);
}
if (pptPath != null) {
lesson.setPptPath(pptPath);
}
if (pptName != null) {
lesson.setPptName(pptName);
}
if (pdfPath != null) {
lesson.setPdfPath(pdfPath);
}
if (pdfName != null) {
lesson.setPdfName(pdfName);
}
if (objectives != null) {
lesson.setObjectives(objectives);
}
if (preparation != null) {
lesson.setPreparation(preparation);
}
if (extension != null) {
lesson.setExtension(extension);
}
if (reflection != null) {
lesson.setReflection(reflection);
}
if (assessmentData != null) {
lesson.setAssessmentData(toValidJsonOrNull(assessmentData));
}
if (useTemplate != null) {
lesson.setUseTemplate(useTemplate);
}
lesson.setUpdatedAt(LocalDateTime.now());
courseLessonMapper.updateById(lesson);
log.info("课程环节更新成功id={}", id);
return lesson;
}
/**
* 删除课程环节
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void delete(Long id) {
log.info("删除课程环节id={}", id);
CourseLesson lesson = courseLessonMapper.selectById(id);
if (lesson == null) {
log.warn("课程环节不存在id={}", id);
throw new BusinessException("课程不存在");
}
courseLessonMapper.deleteById(id);
log.info("课程环节删除成功id={}", id);
}
/**
* 重新排序课程环节
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void reorder(Long courseId, List<Long> lessonIds) {
log.info("重新排序课程环节courseId={}, count={}", courseId, lessonIds.size());
for (int i = 0; i < lessonIds.size(); i++) {
CourseLesson lesson = courseLessonMapper.selectById(lessonIds.get(i));
if (lesson != null && lesson.getCourseId().equals(courseId)) {
lesson.setSortOrder(i + 1);
courseLessonMapper.updateById(lesson);
}
}
log.info("课程环节重新排序成功courseId={}", courseId);
}
/**
* 查询课程环节的教学环节
*/
@Override
public List<LessonStep> findSteps(Long lessonId) {
log.info("查询课程环节的教学环节lessonId={}", lessonId);
List<LessonStep> result = lessonStepMapper.selectList(
new LambdaQueryWrapper<LessonStep>()
.eq(LessonStep::getLessonId, lessonId)
.orderByAsc(LessonStep::getSortOrder)
);
log.info("查询教学环节成功lessonId={}, count={}", lessonId, result.size());
return result;
}
/**
* 创建教学环节
*/
@Override
@Transactional(rollbackFor = Exception.class)
public LessonStep createStep(Long lessonId, String name, String content, Integer duration,
String objective, List<Long> resourceIds) {
log.info("创建教学环节lessonId={}, name={}", lessonId, name);
// 获取最大排序号
Integer maxSortOrder = lessonStepMapper.selectList(
new LambdaQueryWrapper<LessonStep>()
.eq(LessonStep::getLessonId, lessonId)
).stream()
.map(LessonStep::getSortOrder)
.max(Integer::compareTo)
.orElse(0);
LessonStep step = new LessonStep();
step.setLessonId(lessonId);
step.setName(name);
step.setContent(content);
step.setDuration(duration != null ? duration : 5);
step.setObjective(objective);
step.setResourceIds(resourceIds != null ? resourceIds.toString() : null);
step.setSortOrder(maxSortOrder + 1);
step.setCreatedAt(LocalDateTime.now());
lessonStepMapper.insert(step);
// 创建环节资源关联
if (resourceIds != null && !resourceIds.isEmpty()) {
for (int i = 0; i < resourceIds.size(); i++) {
LessonStepResource lsr = new LessonStepResource();
lsr.setStepId(step.getId());
lsr.setResourceId(resourceIds.get(i));
lsr.setSortOrder(i);
lessonStepResourceMapper.insert(lsr);
}
}
log.info("教学环节创建成功id={}, lessonId={}", step.getId(), lessonId);
return step;
}
/**
* 更新教学环节
*/
@Override
@Transactional(rollbackFor = Exception.class)
public LessonStep updateStep(Long stepId, String name, String content, Integer duration,
String objective, List<Long> resourceIds) {
log.info("更新教学环节stepId={}", stepId);
LessonStep step = lessonStepMapper.selectById(stepId);
if (step == null) {
log.warn("教学环节不存在stepId={}", stepId);
throw new BusinessException("教学环节不存在");
}
if (name != null) {
step.setName(name);
}
if (content != null) {
step.setContent(content);
}
if (duration != null) {
step.setDuration(duration);
}
if (objective != null) {
step.setObjective(objective);
}
if (resourceIds != null) {
step.setResourceIds(resourceIds.toString());
// 删除旧的资源关联
lessonStepResourceMapper.delete(
new LambdaQueryWrapper<LessonStepResource>()
.eq(LessonStepResource::getStepId, stepId)
);
// 创建新的资源关联
for (int i = 0; i < resourceIds.size(); i++) {
LessonStepResource lsr = new LessonStepResource();
lsr.setStepId(stepId);
lsr.setResourceId(resourceIds.get(i));
lsr.setSortOrder(i);
lessonStepResourceMapper.insert(lsr);
}
}
step.setUpdatedAt(LocalDateTime.now());
lessonStepMapper.updateById(step);
log.info("教学环节更新成功stepId={}", stepId);
return step;
}
/**
* 删除教学环节
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteStep(Long stepId) {
log.info("删除教学环节stepId={}", stepId);
lessonStepMapper.deleteById(stepId);
log.info("教学环节删除成功stepId={}", stepId);
}
/**
* 重新排序教学环节
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void reorderSteps(Long lessonId, List<Long> stepIds) {
log.info("重新排序教学环节lessonId={}, count={}", lessonId, stepIds.size());
for (int i = 0; i < stepIds.size(); i++) {
LessonStep step = lessonStepMapper.selectById(stepIds.get(i));
if (step != null && step.getLessonId().equals(lessonId)) {
step.setSortOrder(i + 1);
lessonStepMapper.updateById(step);
}
}
log.info("教学环节重新排序成功lessonId={}", lessonId);
}
/**
* assessmentData 转为 MySQL JSON 列可接受的有效 JSON
* 空值返回 null已是有效 JSON 则原样返回否则包装为 JSON 字符串
*/
private String toValidJsonOrNull(String assessmentData) {
if (assessmentData == null || assessmentData.isEmpty()) {
return null;
}
String trimmed = assessmentData.trim();
if (trimmed.isEmpty()) {
return null;
}
// 已是有效 JSON对象或数组则直接使用
if ((trimmed.startsWith("{") && trimmed.endsWith("}")) || (trimmed.startsWith("[") && trimmed.endsWith("]"))) {
try {
JSON.parse(trimmed);
return trimmed;
} catch (Exception ignored) {
// 解析失败当作普通文本处理
}
}
// 普通文本包装为 JSON 字符串
return JSON.toJSONString(assessmentData);
}
/**
* 查询教师的课程环节带权限检查
*/
@Override
public List<CourseLesson> findCourseLessonsForTeacher(Long courseId, Long tenantId) {
log.info("查询教师的课程环节courseId={}, tenantId={}", courseId, tenantId);
// 检查租户是否有权限访问该课程
TenantCourse tenantCourse = tenantCourseMapper.selectOne(
new LambdaQueryWrapper<TenantCourse>()
.eq(TenantCourse::getTenantId, tenantId)
.eq(TenantCourse::getCourseId, courseId)
.eq(TenantCourse::getEnabled, 1)
);
if (tenantCourse == null) {
// 检查是否通过套餐授权
Long count = tenantPackageMapper.selectCount(
new LambdaQueryWrapper<TenantPackage>()
.eq(TenantPackage::getTenantId, tenantId)
.eq(TenantPackage::getStatus, TenantPackageStatus.ACTIVE)
);
if (count == 0) {
log.warn("无权访问该课程courseId={}, tenantId={}", courseId, tenantId);
throw new BusinessException("无权访问该课程");
}
}
List<CourseLesson> result = findByCourseId(courseId);
log.info("查询教师的课程环节成功courseId={}, count={}", courseId, result.size());
return result;
}
}

View File

@ -1,286 +0,0 @@
package com.reading.platform.service.impl;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.reading.platform.common.exception.BusinessException;
import com.reading.platform.entity.ResourceItem;
import com.reading.platform.entity.ResourceLibrary;
import com.reading.platform.mapper.ResourceItemMapper;
import com.reading.platform.mapper.ResourceLibraryMapper;
import com.reading.platform.service.ResourceLibraryService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 资源库服务实现类
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class ResourceLibraryServiceImpl extends ServiceImpl<ResourceLibraryMapper, ResourceLibrary> implements ResourceLibraryService {
private final ResourceLibraryMapper libraryMapper;
private final ResourceItemMapper itemMapper;
/**
* 分页查询资源库
*/
@Override
public Page<ResourceLibrary> findAllLibraries(String libraryType, String keyword, Integer page, Integer pageSize) {
log.info("查询资源库列表libraryType={}, keyword={}, page={}, pageSize={}", libraryType, keyword, page, pageSize);
Page<ResourceLibrary> pageParam = new Page<>(page, pageSize);
LambdaQueryWrapper<ResourceLibrary> wrapper = new LambdaQueryWrapper<>();
if (libraryType != null && !libraryType.isEmpty()) {
wrapper.eq(ResourceLibrary::getLibraryType, libraryType);
}
if (keyword != null && !keyword.isEmpty()) {
wrapper.like(ResourceLibrary::getName, keyword);
}
wrapper.orderByDesc(ResourceLibrary::getCreatedAt);
Page<ResourceLibrary> result = libraryMapper.selectPage(pageParam, wrapper);
log.info("查询资源库列表成功,总数={}", result.getTotal());
return result;
}
/**
* 查询资源库详情
*/
@Override
public ResourceLibrary findLibraryById(String id) {
log.info("查询资源库详情id={}", id);
ResourceLibrary library = libraryMapper.selectById(id);
if (library == null) {
log.warn("资源库不存在id={}", id);
throw new BusinessException("资源库不存在");
}
return library;
}
/**
* 查询资源库不存在时返回 null
*/
@Override
public ResourceLibrary findLibraryByIdOrNull(String id) {
if (id == null || id.isEmpty()) return null;
return libraryMapper.selectById(id);
}
/**
* 创建资源库
*/
@Override
public ResourceLibrary createLibrary(String name, String type, String description, String tenantId) {
log.info("创建资源库name={}, type={}, tenantId={}", name, type, tenantId);
ResourceLibrary library = new ResourceLibrary();
library.setName(name);
library.setLibraryType(type);
library.setDescription(description);
library.setTenantId(tenantId);
library.setStatus("ACTIVE");
library.setSortOrder(0);
libraryMapper.insert(library);
log.info("资源库创建成功id={}", library.getId());
return library;
}
/**
* 更新资源库
*/
@Override
public ResourceLibrary updateLibrary(String id, String name, String description) {
log.info("更新资源库id={}, name={}, description={}", id, name, description);
ResourceLibrary library = libraryMapper.selectById(id);
if (library == null) {
log.warn("资源库不存在id={}", id);
throw new BusinessException("资源库不存在");
}
if (name != null) {
library.setName(name);
}
if (description != null) {
library.setDescription(description);
}
libraryMapper.updateById(library);
log.info("资源库更新成功id={}", id);
return library;
}
/**
* 删除资源库
*/
@Override
public void deleteLibrary(String id) {
log.info("删除资源库id={}", id);
// 先删除该资源库下的所有资源项
LambdaQueryWrapper<ResourceItem> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(ResourceItem::getLibraryId, id);
itemMapper.delete(wrapper);
libraryMapper.deleteById(id);
log.info("资源库删除成功id={}", id);
}
/**
* 分页查询资源项目
*/
@Override
public Page<ResourceItem> findAllItems(String libraryId, String fileType, String keyword, Integer page, Integer pageSize) {
log.info("查询资源项目列表libraryId={}, fileType={}, keyword={}, page={}, pageSize={}", libraryId, fileType, keyword, page, pageSize);
Page<ResourceItem> pageParam = new Page<>(page, pageSize);
LambdaQueryWrapper<ResourceItem> wrapper = new LambdaQueryWrapper<>();
if (libraryId != null && !libraryId.isEmpty()) {
wrapper.eq(ResourceItem::getLibraryId, libraryId);
}
if (fileType != null && !fileType.isEmpty()) {
wrapper.eq(ResourceItem::getFileType, fileType);
}
if (keyword != null && !keyword.isEmpty()) {
wrapper.and(w -> w.like(ResourceItem::getTitle, keyword)
.or()
.like(ResourceItem::getDescription, keyword));
}
wrapper.orderByDesc(ResourceItem::getCreatedAt);
Page<ResourceItem> result = itemMapper.selectPage(pageParam, wrapper);
log.info("查询资源项目列表成功,总数={}", result.getTotal());
return result;
}
/**
* 查询资源项目详情
*/
@Override
public ResourceItem findItemById(String id) {
log.info("查询资源项目详情id={}", id);
ResourceItem item = itemMapper.selectById(id);
if (item == null) {
log.warn("资源项目不存在id={}", id);
throw new BusinessException("资源项目不存在");
}
return item;
}
/**
* 创建资源项目数字资源
*/
@Override
public ResourceItem createItem(String libraryId, String title, String fileType, String filePath,
Long fileSize, String description, String tags, String tenantId) {
log.info("创建资源项目libraryId={}, title={}, fileType={}, fileSize={}, tenantId={}", libraryId, title, fileType, fileSize, tenantId);
ResourceItem item = new ResourceItem();
item.setId(null); // 确保由数据库 AUTO_INCREMENT 生成避免主键冲突
item.setLibraryId(libraryId); // 目标资源库 ID
item.setTitle(title);
item.setName(title); // 数据库 name 字段 NOT NULL title 保持一致
item.setDescription(description);
item.setFileType(fileType);
item.setFilePath(filePath);
item.setFileSize(fileSize);
item.setTags(tags);
item.setSortOrder(0);
item.setTenantId(tenantId);
item.setStatus("ACTIVE");
itemMapper.insert(item);
log.info("资源项目创建成功id={}", item.getId());
return item;
}
/**
* 更新资源项目数字资源
*/
@Override
public ResourceItem updateItem(String id, String title, String description, String tags) {
log.info("更新资源项目id={}, title={}, description={}, tags={}", id, title, description, tags);
ResourceItem item = itemMapper.selectById(id);
if (item == null) {
log.warn("资源项目不存在id={}", id);
throw new BusinessException("资源项目不存在");
}
if (title != null) {
item.setTitle(title);
}
if (description != null) {
item.setDescription(description);
}
if (tags != null) {
item.setTags(tags);
}
itemMapper.updateById(item);
log.info("资源项目更新成功id={}", id);
return item;
}
/**
* 删除资源项目
*/
@Override
public void deleteItem(String id) {
log.info("删除资源项目id={}", id);
itemMapper.deleteById(id);
log.info("资源项目删除成功id={}", id);
}
/**
* 批量删除资源项目
*/
@Override
public void batchDeleteItems(List<String> ids) {
log.info("批量删除资源项目ids={}", ids);
itemMapper.deleteBatchIds(ids);
log.info("批量删除资源项目成功,数量={}", ids.size());
}
/**
* 获取统计数据
*/
@Override
public Map<String, Object> getStats() {
log.info("获取资源库统计数据");
Map<String, Object> stats = new HashMap<>();
Long libraryCount = libraryMapper.selectCount(null);
Long itemCount = itemMapper.selectCount(null);
stats.put("totalLibraries", libraryCount);
stats.put("totalItems", itemCount);
// 按资源库类型统计资源数量绘本资源教学材料等
Map<String, Long> itemsByLibraryType = new HashMap<>();
LambdaQueryWrapper<ResourceLibrary> libWrapper = new LambdaQueryWrapper<>();
for (ResourceLibrary lib : libraryMapper.selectList(libWrapper)) {
String type = lib.getLibraryType();
if (type != null) {
LambdaQueryWrapper<ResourceItem> itemWrapper = new LambdaQueryWrapper<>();
itemWrapper.eq(ResourceItem::getLibraryId, String.valueOf(lib.getId()));
long count = itemMapper.selectCount(itemWrapper);
itemsByLibraryType.merge(type, count, Long::sum);
}
}
stats.put("itemsByLibraryType", itemsByLibraryType);
log.info("资源库统计数据totalLibraries={}, totalItems={}, itemsByLibraryType={}", libraryCount, itemCount, itemsByLibraryType);
return stats;
}
}

View File

@ -1,149 +0,0 @@
package com.reading.platform.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.reading.platform.common.exception.BusinessException;
import com.reading.platform.entity.CoursePackage;
import com.reading.platform.entity.Theme;
import com.reading.platform.mapper.CoursePackageMapper;
import com.reading.platform.mapper.ThemeMapper;
import com.reading.platform.service.ThemeService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* 主题字典服务实现类
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class ThemeServiceImpl extends ServiceImpl<ThemeMapper, Theme> implements ThemeService {
private final ThemeMapper themeMapper;
private final CoursePackageMapper courseMapper;
/**
* 查询所有主题
*/
@Override
public List<Theme> findAll() {
log.info("查询所有主题");
List<Theme> result = lambdaQuery()
.orderByAsc(Theme::getSortOrder)
.list();
log.info("查询所有主题成功count={}", result.size());
return result;
}
/**
* 查询主题详情
*/
@Override
public Theme findById(Long id) {
log.info("查询主题详情id={}", id);
Theme theme = themeMapper.selectById(id);
if (theme == null) {
log.warn("主题不存在id={}", id);
throw new BusinessException("主题不存在");
}
return theme;
}
/**
* 创建主题
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Theme create(String name, String description, Integer sortOrder) {
log.info("创建主题name={}, sortOrder={}", name, sortOrder);
// 获取最大排序号
Integer maxSortOrder = themeMapper.selectList(null)
.stream()
.map(Theme::getSortOrder)
.max(Integer::compareTo)
.orElse(0);
Theme theme = new Theme();
theme.setName(name);
theme.setDescription(description);
theme.setSortOrder(sortOrder != null ? sortOrder : maxSortOrder + 1);
theme.setStatus("ACTIVE");
themeMapper.insert(theme);
log.info("主题创建成功id={}", theme.getId());
return theme;
}
/**
* 更新主题
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Theme update(Long id, String name, String description, Integer sortOrder, String status) {
log.info("更新主题id={}, name={}, status={}", id, name, status);
Theme theme = themeMapper.selectById(id);
if (theme == null) {
log.warn("主题不存在id={}", id);
throw new BusinessException("主题不存在");
}
if (name != null) {
theme.setName(name);
}
if (description != null) {
theme.setDescription(description);
}
if (sortOrder != null) {
theme.setSortOrder(sortOrder);
}
if (status != null) {
theme.setStatus(status);
}
themeMapper.updateById(theme);
log.info("主题更新成功id={}", id);
return theme;
}
/**
* 删除主题
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void delete(Long id) {
log.info("删除主题id={}", id);
// 检查是否有关联课程
Long courseCount = courseMapper.selectCount(
new LambdaQueryWrapper<CoursePackage>().eq(CoursePackage::getThemeId, id)
);
if (courseCount > 0) {
log.warn("删除主题失败,该主题下有 {} 个课程包id={}", courseCount, id);
throw new BusinessException("该主题下有 " + courseCount + " 个课程包,无法删除");
}
themeMapper.deleteById(id);
log.info("主题删除成功id={}", id);
}
/**
* 重新排序
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void reorder(List<Long> ids) {
log.info("重新排序主题count={}", ids.size());
for (int i = 0; i < ids.size(); i++) {
Theme theme = themeMapper.selectById(ids.get(i));
if (theme != null) {
theme.setSortOrder(i + 1);
themeMapper.updateById(theme);
}
}
log.info("主题重新排序成功");
}
}