refactor: 根包由 com.bestsch.bschclassmanage 重命名为 com.yunhai.bschclassmanage

- git mv src/main/java/com/bestsch/bschclassmanage -> src/main/java/com/yunhai/bschclassmanage
- 255 个 Java 文件的 package/import 同步替换为 com.yunhai.bschclassmanage
- pom.xml 项目 groupId 设为 com.yunhai(与外部依赖 com.bestsch:bsch-* 无关,未改动)
- IDE 缓存 workspace.xml 路径同步更新(外部依赖坐标 com.bestsch:bsch-* 保持不动)
This commit is contained in:
Allen
2026-08-12 17:21:22 +08:00
parent 66cd0f1515
commit dc217ca7a9
258 changed files with 1082 additions and 1067 deletions
@@ -0,0 +1,466 @@
package com.yunhai.bschclassmanage.service.impl;
import com.alibaba.fastjson.JSON;
import com.yunhai.bschclassmanage.Repository.*;
import com.yunhai.bschclassmanage.annotation.OperationLog;
import com.yunhai.bschclassmanage.entity.*;
import com.yunhai.bschclassmanage.enums.CourseType;
import com.yunhai.bschclassmanage.model.constant.LogModule;
import com.yunhai.bschclassmanage.model.dto.SchDicCourseDTO;
import com.yunhai.bschclassmanage.model.entityField.EntityFieldNames;
import com.yunhai.bschclassmanage.model.vo.PageVO;
import com.yunhai.bschclassmanage.model.vo.SchDicCourseVO;
import com.yunhai.bschclassmanage.service.ISchDicCourseService;
import com.yunhai.bschclassmanage.service.ISchDicDivisionService;
import com.yunhai.bschclassmanage.service.ISchDicMajorsService;
import com.yunhai.bschclassmanage.utils.*;
import com.yunhai.platform.utils.exception.DException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.criteria.Predicate;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
@Slf4j
@Service
public class SchDicCourseService implements ISchDicCourseService {
@Autowired
private SchDicCourseRepository schDicCourseRepository;
@Autowired
private SchDicCourseMajorsRepository schDicCourseMajorsRepository;
@Autowired
private SchDicCourseDivisionRepository schDicCourseDivisionRepository;
@Autowired
private ISchDicMajorsService schDicMajorsService;
@Autowired
private ISchDicDivisionService schDicDivisionService;
@Autowired
private RedisUtil redisUtil;
public List<SchDicCourse> getSchDicCourseList(Integer schId) {
Specification<SchDicCourse> spec = (root, query, cb) -> {
List<Predicate> predicates = new ArrayList<>();
predicates.add(cb.equal(root.get(EntityFieldNames.schId), schId));
return cb.and(predicates.toArray(new Predicate[0]));
};
return schDicCourseRepository.findAll(spec);
}
public List<SchDicCourse> getSchDicCourseCacheList(Integer schId) {
String cacheKey = RedisConstants.SCHOOL_COURSE + "_" + schId;
if (redisUtil.hasKey(cacheKey)) {
// 注意:返回副本或不可变列表,避免外部修改影响缓存
return redisUtil.getList(cacheKey,SchDicCourse.class);
}
// 同步查询数据库,防止缓存击穿
synchronized (this) {
if (redisUtil.hasKey(cacheKey)) {
return redisUtil.getList(cacheKey,SchDicCourse.class);
}
List<SchDicCourse> list = getSchDicCourseList(schId);
redisUtil.set(cacheKey, new ArrayList<>(list), RedisConstants.CACHE_EXPIRE_30D);
return list;
}
}
public PageVO<SchDicCourseVO> search(Integer schId, Integer type, String keyword,
Integer divisionId, Integer majorsId,
Pageable pageable) {
// 1. 查询课程分页(动态条件已在 Repository 中处理)
Page<SchDicCourse> coursePage = schDicCourseRepository.findCoursesByConditions(
schId, type, keyword, divisionId, majorsId, pageable
);
if (coursePage.isEmpty()) {
return PageVO.empty(pageable.getPageSize());
}
// 2. 提取课程 ID 列表
List<Integer> courseIds = coursePage.getContent().stream()
.map(SchDicCourse::getId)
.collect(Collectors.toList());
// 3. 批量加载关联关系(按课程ID分组)
Map<Integer, List<SchDicCourseMajors>> majorsGroup = loadMajorsGroup(courseIds, schId);
Map<Integer, List<SchDicCourseDivision>> divisionGroup = loadDivisionGroup(courseIds, schId);
// 4. 获取当前学校的所有专业和学部(用于填充实体详情)
Map<Integer, SchDicMajors> majorsMap = schDicMajorsService.getSchDicMajorsCacheList(schId)
.stream()
.collect(Collectors.toMap(SchDicMajors::getId, Function.identity()));
Map<Integer, SchDicDivision> divisionMap = schDicDivisionService.getSchDicDivisionCacheList(schId)
.stream()
.collect(Collectors.toMap(SchDicDivision::getId, Function.identity()));
// 5. 转换每个课程为 VO
List<SchDicCourseVO> voList = coursePage.getContent().stream()
.map(course -> convertToVO(course, majorsGroup, divisionGroup, majorsMap, divisionMap))
.collect(Collectors.toList());
return new PageVO<>(pageable.getPageNumber() + 1,
pageable.getPageSize(),
coursePage.getTotalElements(),
voList);
}
@OperationLog(module = LogModule.COURSE)
@Transactional(rollbackFor = Exception.class)
public void saveSchCourse(UserContext.UserInfo user, SchDicCourseDTO dto) {
Integer schId = user.getSchId();
// 1. 基础校验
if (dto == null) {
throw new IllegalArgumentException("课程数据不能为空");
}
if (StringUtils.isBlank(dto.getName())) {
throw new IllegalArgumentException("课程名称不能为空");
}
// 2. VO → Entity 映射
SchDicCourse entity = ModelMapperUtil.map(dto, SchDicCourse.class);
// 3. 名称唯一性校验
boolean isNew = (entity.getId() == null || entity.getId() == 0);
boolean nameExists = isNew ?
schDicCourseRepository.existsBySchIdAndName(schId, entity.getName()) :
schDicCourseRepository.existsBySchIdAndNameAndIdNot(schId, entity.getName(), entity.getId());
if (nameExists) {
throw new IllegalArgumentException("课程名称已存在:" + entity.getName());
}
SchDicCourse oldEntity = null;
Set<Integer> oldMajors = null;
Set<Integer> oldDivisions = null;
// 4. 执行保存(新增或更新)
if (isNew) {
OtherUtil.setBaseEntityValue(user, entity); // 设置创建/更新审计字段
entity = schDicCourseRepository.save(entity);
} else {
SchDicCourse existing = schDicCourseRepository.findById(dto.getId())
.orElseThrow(() -> new IllegalArgumentException("课程不存在,ID=" + dto.getId()));
oldEntity=OtherUtil.deepCopy(existing,SchDicCourse.class);
oldMajors = loadMajorsIdSet(dto.getId(),user.getSchId());
oldDivisions = loadDivisionsIdSet(dto.getId(),user.getSchId());
// 复制允许修改的字段(忽略 id, schId, 创建审计字段)
OtherUtil.setBaseEntityValue(user, entity); // 设置创建/更新审计字段
BeanUtils.copyProperties(entity, existing,
EntityFieldNames.id,
EntityFieldNames.schId,
EntityFieldNames.createDate,
EntityFieldNames.createUserId);
// 更新时需单独设置 updateDate / updateUserId(假设 setBaseEntityValue 已处理)
entity = schDicCourseRepository.save(existing);
}
// 5. 维护关联关系(删除旧关联 → 添加新关联)
Integer courseId = entity.getId();
// 5.1 删除旧的关联
deleteRelations(courseId, schId);
// 5.2 保存新的关联
saveRelations(courseId, schId, dto);
// 6. 清除缓存
String cacheKey = RedisConstants.SCHOOL_COURSE + "_" + schId;
redisUtil.del(cacheKey);
{
String description = buildCourseLogDescription(entity, isNew, oldEntity, dto,
oldMajors, oldDivisions,
schDicMajorsService.toIdAndNameMap(user.getSchId()),
schDicDivisionService.toIdAndNameMap(user.getSchId()));
LogContextHolder.setDescription(description);
}
}
/**
* 构建课程操作的详细日志描述
*/
private String buildCourseLogDescription(SchDicCourse newEntity, boolean isNew,
SchDicCourse oldEntity, SchDicCourseDTO dto,
Set<Integer> oldMajors, Set<Integer> oldDivisions,
Map<Integer, String> majorsNameMap,
Map<Integer, String> divisionNameMap) {
try {
List<String> parts = new ArrayList<>();
// ========== 1. 课程本身的变化 ==========
if (isNew) {
parts.add(String.format("新增课程:%s(编码:%s,类型:%s,别称:%s)",
newEntity.getName(),
newEntity.getCode() != null ? newEntity.getCode() : "无",
CourseType.nameById(newEntity.getType()),
newEntity.getNickname() != null ? newEntity.getNickname() : "无"));
} else {
List<String> changes = new ArrayList<>();
if (!Objects.equals(oldEntity.getName(), newEntity.getName())) {
changes.add(String.format("名称从 '%s' 修改为 '%s'", oldEntity.getName(), newEntity.getName()));
}
if (!Objects.equals(oldEntity.getCode(), newEntity.getCode())) {
changes.add(String.format("编码从 '%s' 修改为 '%s'", oldEntity.getCode(), newEntity.getCode()));
}
if (!Objects.equals(oldEntity.getNickname(), newEntity.getNickname())) {
changes.add(String.format("别称从 '%s' 修改为 '%s'", oldEntity.getNickname(), newEntity.getNickname()));
}
if (!Objects.equals(oldEntity.getType(), newEntity.getType())) {
changes.add(String.format("类型从 '%s' 修改为 '%s'",
CourseType.nameById(oldEntity.getType()),
CourseType.nameById(newEntity.getType())));
}
if (changes.isEmpty()) {
parts.add("课程信息无变更");
} else {
parts.add("课程变更:" + String.join(";", changes));
}
}
// ========== 2. 关联关系变化(专业) ==========
Set<Integer> newMajors = dto.getMajorsIds();
if (!isNew && oldMajors != null) {
Set<Integer> addedMajors = new HashSet<>(newMajors);
addedMajors.removeAll(oldMajors);
Set<Integer> removedMajors = new HashSet<>(oldMajors);
removedMajors.removeAll(newMajors);
if (!addedMajors.isEmpty()) {
parts.add("绑定专业:" + getNames(addedMajors, majorsNameMap));
}
if (!removedMajors.isEmpty()) {
parts.add("解绑专业:" + getNames(removedMajors, majorsNameMap));
}
} else if (isNew && newMajors != null && !newMajors.isEmpty()) {
parts.add("绑定专业:" + getNames(newMajors, majorsNameMap));
}
// ========== 3. 关联关系变化(学部) ==========
Set<Integer> newDivisions = dto.getDivisionIds();
if (!isNew && oldDivisions != null) {
Set<Integer> addedDivs = new HashSet<>(newDivisions);
addedDivs.removeAll(oldDivisions);
Set<Integer> removedDivs = new HashSet<>(oldDivisions);
removedDivs.removeAll(newDivisions);
if (!addedDivs.isEmpty()) {
parts.add("绑定学部:" + getNames(addedDivs, divisionNameMap));
}
if (!removedDivs.isEmpty()) {
parts.add("解绑学部:" + getNames(removedDivs, divisionNameMap));
}
} else if (isNew && newDivisions != null && !newDivisions.isEmpty()) {
parts.add("绑定学部:" + getNames(newDivisions, divisionNameMap));
}
return String.join(";", parts);
} catch (Exception e) {
// 日志构建失败时返回简短描述,不影响主流程
log.error("构建课程日志描述失败:{}", e.getMessage());
return isNew ? "新增课程(日志生成失败)" : "更新课程(日志生成失败)";
}
}
/**
* 将 Set<Integer> 转换为名称字符串(用"、"分隔)
*/
private String getNames(Set<Integer> ids, Map<Integer, String> nameMap) {
if (ids == null || ids.isEmpty()) return "无";
return ids.stream()
.map(id -> nameMap.getOrDefault(id, String.valueOf(id)))
.collect(Collectors.joining("、"));
}
@OperationLog(module = LogModule.COURSE)
@Transactional(rollbackFor = Exception.class)
public void delete(UserContext.UserInfo user,Integer id){
SchDicCourse entity= schDicCourseRepository.findByIdAndSchId(id,user.getSchId())
.orElseThrow(() -> new DException( "课程不存在"));
String cacheKey = RedisConstants.SCHOOL_COURSE + "_" + user.getSchId();
redisUtil.del(cacheKey);
deleteRelations(id, user.getSchId());
schDicCourseRepository.deleteById(id);
String description = String.format("删除课程:%s(编码:%s,类型:%s)",
entity.getName(),
entity.getCode() != null ? entity.getCode() : "无",
CourseType.nameById(entity.getType()));
LogContextHolder.setDescription(description);
log.debug("操作人:{},删除课程日志:{}", user.getUserName(), JSON.toJSONString(entity));
}
// ==================== 私有辅助方法 ====================
/**
* 批量删除课程-专业、课程-学部关联
*/
private void deleteRelations(Integer courseId, Integer schId) {
schDicCourseMajorsRepository.deleteByCourseIdAndSchId(courseId, schId);
schDicCourseDivisionRepository.deleteByCourseIdAndSchId(courseId, schId);
}
/**
* 批量保存新的课程-专业、课程-学部关联
*/
private void saveRelations(Integer courseId, Integer schId, SchDicCourseDTO dto) {
// 保存专业关联
if (CollectionUtils.isNotEmpty(dto.getMajorsIds())) {
List<SchDicCourseMajors> majorsList = dto.getMajorsIds().stream()
.filter(Objects::nonNull)
.map(id -> {
SchDicCourseMajors rel = new SchDicCourseMajors();
rel.setCourseId(courseId);
rel.setMajorsId(id);
rel.setSchId(schId);
return rel;
})
.collect(Collectors.toList());
if (!majorsList.isEmpty()) {
schDicCourseMajorsRepository.saveAll(majorsList);
}
}
// 保存学部关联
if (CollectionUtils.isNotEmpty(dto.getDivisionIds())) {
List<SchDicCourseDivision> divisionList = dto.getDivisionIds().stream()
.filter(Objects::nonNull)
.map(id -> {
SchDicCourseDivision rel = new SchDicCourseDivision();
rel.setCourseId(courseId);
rel.setDivisionId(id);
rel.setSchId(schId);
return rel;
})
.collect(Collectors.toList());
if (!divisionList.isEmpty()) {
schDicCourseDivisionRepository.saveAll(divisionList);
}
}
}
/**
* 批量加载课程-专业关联关系,并按 courseId 分组
*/
private Map<Integer, List<SchDicCourseMajors>> loadMajorsGroup(List<Integer> courseIds, Integer schId) {
if (courseIds.isEmpty()) {
return Collections.emptyMap();
}
return schDicCourseMajorsRepository.findByCourseIdInAndSchId(courseIds, schId)
.stream()
.collect(Collectors.groupingBy(SchDicCourseMajors::getCourseId));
}
/**
* 加载指定课程关联的专业列表(单个)
*/
private List<SchDicCourseMajors> loadMajorsList(Integer courseId, Integer schId) {
if (courseId == null) {
return Collections.emptyList();
}
return schDicCourseMajorsRepository.findByCourseIdAndSchId(courseId, schId);
}
/**
* 加载指定课程关联的专业ID集合(用于日志)
*/
private Set<Integer> loadMajorsIdSet(Integer courseId, Integer schId) {
return loadMajorsList(courseId, schId).stream()
.map(SchDicCourseMajors::getMajorsId)
.collect(Collectors.toSet());
}
/**
* 批量加载课程-学部关联关系,并按 courseId 分组
*/
private Map<Integer, List<SchDicCourseDivision>> loadDivisionGroup(List<Integer> courseIds, Integer schId) {
if (courseIds.isEmpty()) {
return Collections.emptyMap();
}
return schDicCourseDivisionRepository.findByCourseIdInAndSchId(courseIds, schId)
.stream()
.collect(Collectors.groupingBy(SchDicCourseDivision::getCourseId));
}
private List<SchDicCourseDivision> loadDivisionsList(Integer courseId, Integer schId) {
if (courseId == null) {
return Collections.emptyList();
}
return schDicCourseDivisionRepository.findByCourseIdAndSchId(courseId, schId);
}
private Set<Integer> loadDivisionsIdSet(Integer courseId, Integer schId) {
return loadDivisionsList(courseId, schId).stream()
.map(SchDicCourseDivision::getDivisionId)
.collect(Collectors.toSet());
}
/**
* 将单个课程实体转换为 VO,并填充关联列表
*/
private SchDicCourseVO convertToVO(SchDicCourse course,
Map<Integer, List<SchDicCourseMajors>> majorsGroup,
Map<Integer, List<SchDicCourseDivision>> divisionGroup,
Map<Integer, SchDicMajors> majorsEntityMap,
Map<Integer, SchDicDivision> divisionEntityMap) {
// 使用 ModelMapperUtil 复制基础字段
SchDicCourseVO vo = ModelMapperUtil.map(course, SchDicCourseVO.class);
if (vo == null&& course != null) throw new DException("数据转换失败");
// 填充 typeName
vo.setTypeName(CourseType.nameById(course.getType()));
// 填充 majorsList
List<SchDicCourseMajors> majorsRel = majorsGroup.getOrDefault(course.getId(), Collections.emptyList());
List<SchDicMajors> majors = majorsRel.stream()
.map(rel -> majorsEntityMap.get(rel.getMajorsId()))
.filter(Objects::nonNull)
.collect(Collectors.toList());
// 按 sort 升序,null 值排在最后
majors.sort(Comparator.comparing(SchDicMajors::getSort, Comparator.nullsLast(Comparator.naturalOrder())));
vo.setMajorsList(majors);
// 填充 divisionList
List<SchDicCourseDivision> divisionRel = divisionGroup.getOrDefault(course.getId(), Collections.emptyList());
List<SchDicDivision> divisions = divisionRel.stream()
.map(rel -> divisionEntityMap.get(rel.getDivisionId()))
.filter(Objects::nonNull)
.collect(Collectors.toList());
// 按 sort 升序,null 值排在最后
divisions.sort(Comparator.comparing(SchDicDivision::getSort, Comparator.nullsLast(Comparator.naturalOrder())));
vo.setDivisionList(divisions);
return vo;
}
}