package com.yunhai.bschclassmanage.service.impl; import com.alibaba.fastjson.JSON; import com.yunhai.bschclassmanage.Repository.SchClassRepository; import com.yunhai.bschclassmanage.Repository.SchDicCampusRepository; import com.yunhai.bschclassmanage.annotation.OperationLog; import com.yunhai.bschclassmanage.entity.SchDicCampus; import com.yunhai.bschclassmanage.model.constant.LogModule; import com.yunhai.bschclassmanage.model.entityField.EntityFieldNames; import com.yunhai.bschclassmanage.model.vo.SelectIntVO; import com.yunhai.bschclassmanage.service.ISchDicCampusService; import com.yunhai.bschclassmanage.utils.*; import com.yunhai.platform.utils.exception.DException; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; 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 SchDicCampusService implements ISchDicCampusService { @Autowired private SchDicCampusRepository schDicCampusRepository; @Autowired private SchClassRepository schClassRepository; @Autowired private RedisUtil redisUtil; public List getSchDicCampusList(Integer schId) { Specification spec = (root, query, cb) -> { List predicates = new ArrayList<>(); predicates.add(cb.equal(root.get(EntityFieldNames.schId), schId)); return cb.and(predicates.toArray(new Predicate[0])); }; return schDicCampusRepository.findAll(spec); } // 获取学校院校列表(带缓存) public List getSchDicCampusCacheList(Integer schId) { String cacheKey = RedisConstants.SCHOOL_CAMPUS + "_" + schId; if (redisUtil.hasKey(cacheKey)) { // 注意:返回副本或不可变列表,避免外部修改影响缓存 return redisUtil.getList(cacheKey,SchDicCampus.class); } // 同步查询数据库,防止缓存击穿 synchronized (this) { if (redisUtil.hasKey(cacheKey)) { return redisUtil.getList(cacheKey,SchDicCampus.class); } List list = getSchDicCampusList(schId); // 根据 sort 字段升序排序,null 值排在最后 list.sort(Comparator.comparing(SchDicCampus::getSort, Comparator.nullsLast(Comparator.naturalOrder()))); redisUtil.set(cacheKey, new ArrayList<>(list), RedisConstants.CACHE_EXPIRE_30D); return list; } } @OperationLog(module = LogModule.CAMPUS) @Transactional(rollbackFor = Exception.class) public void saveSchCampus(UserContext.UserInfo user, SchDicCampus entity) { Integer schId=user.getSchId(); boolean isNew=entity.getId() == null || entity.getId() == 0; // 参数校验 if (entity.getName() == null || entity.getName().trim().isEmpty()) throw new DException("校区名称不能为空"); boolean nameExists; SchDicCampus oldEntity=null; if (isNew) { // 新增:检查同名校区 nameExists = schDicCampusRepository.existsBySchIdAndName(schId, entity.getName()); if (nameExists) { throw new DException("校区名称已存在:" + entity.getName()); } OtherUtil.setBaseEntityValue(user, entity); schDicCampusRepository.save(entity); } else { // 更新:排除自身,检查其他校区同名 nameExists = schDicCampusRepository.existsBySchIdAndNameAndIdNot(schId, entity.getName(), entity.getId()); if (nameExists) { throw new DException("校区名称已存在:" + entity.getName()); } Integer entityId=entity.getId(); SchDicCampus existing = schDicCampusRepository.findById(entityId) .orElseThrow(() -> new DException("校区不存在,ID=" + entityId)); oldEntity=OtherUtil.deepCopy(existing,SchDicCampus.class); OtherUtil.setBaseEntityValue(user, entity); BeanUtils.copyProperties(entity, existing, EntityFieldNames.id, EntityFieldNames.schId, EntityFieldNames.code, EntityFieldNames.createDate, EntityFieldNames.createUserId); entity=schDicCampusRepository.save(existing); } // 清除缓存 String cacheKey = RedisConstants.SCHOOL_CAMPUS + "_" + schId; redisUtil.del(cacheKey); { String description = buildCampusLogDescription(entity,isNew,oldEntity); LogContextHolder.setDescription(description); } } /** * 构建学部操作的详细日志描述 */ private String buildCampusLogDescription(SchDicCampus newEntity,boolean isNew,SchDicCampus oldEntity) { if (isNew) { return String.format("新增校区:%s(序号:%d)", newEntity.getName(), newEntity.getSort() != null ? newEntity.getSort() : 0); } else { List changes = new ArrayList<>(); if (!Objects.equals(oldEntity.getName(), newEntity.getName())) { changes.add(String.format("名称从 '%s' 修改为 '%s'", oldEntity.getName(), newEntity.getName())); } if (!Objects.equals(oldEntity.getSort(), newEntity.getSort())) { changes.add(String.format("序号从 %d 修改为 %d", oldEntity.getSort(), newEntity.getSort())); } if (changes.isEmpty()) { return "更新校区:无实际变更"; } return "更新校区:" + String.join(";", changes); } } @OperationLog(module = LogModule.CAMPUS) @Transactional(rollbackFor = Exception.class) public void sortSchCampus(UserContext.UserInfo user,List ids) { List campusList = getSchDicCampusCacheList(user.getSchId()); Map campusMap = campusList.stream() .collect(Collectors.toMap(SchDicCampus::getId, Function.identity())); Date now = new Date(); // 1. 按传入顺序更新 sort 值 int sortOrder = 1; for (Integer id : ids) { SchDicCampus campus = campusMap.get(id); if (campus != null) { campus.setSort(sortOrder++); campus.setUpdateDate(now); campus.setUpdateUserId(user.getUserId()); } } // 2. 处理未出现在排序列表中的校区(设置 sort = null 或一个大值) for (SchDicCampus campus : campusList) { if (!ids.contains(campus.getId())) { campus.setSort(null); // 或者设置一个最大值,比如 999 campus.setUpdateDate(now); campus.setUpdateUserId(user.getUserId()); } } // 批量保存 schDicCampusRepository.saveAll(campusList); String cacheKey = RedisConstants.SCHOOL_CAMPUS + "_" + user.getSchId(); redisUtil.del(cacheKey); { String names = ids.stream() .map(id -> { SchDicCampus m = campusMap.get(id); return m != null ? m.getName() : String.valueOf(id); }) .collect(Collectors.joining("、")); String description = String.format("调整校区排序:共 %d 个专业(列表:%s)", ids.size(), names); LogContextHolder.setDescription(description); } } /** * 删除校区 * 1. 参数非空校验 * 2. 校验校区是否存在(匹配学校+校区ID,防止跨学校删除) * 3. 校验校区是否绑定班级,存在关联则禁止删除 * 4. 删除数据库数据 + 清除对应学校校区缓存 * @param id 校区ID */ @OperationLog(module = LogModule.CAMPUS) @Transactional(rollbackFor = Exception.class) public void delete(UserContext.UserInfo user, Integer id) { // 1. 参数合法性校验 if (user.getSchId() == null || id == null) { throw new DException("学校ID、校区ID不能为空"); } // 2. 校验当前学校下该校区是否存在,防止越权删除其他学校校区 SchDicCampus campus = schDicCampusRepository.findByIdAndSchId(id, user.getSchId()) .orElseThrow(() -> new DException("校区不存在,无法删除")); // 3. 业务校验:判断校区是否关联班级,有班级则禁止删除 long bindClassCount = schClassRepository.countByCampusId(id); if (bindClassCount > 0) { throw new DException(String.format("该校区下存在%s个班级,不可删除,请先移除班级关联", bindClassCount)); } // 4. 删除数据 schDicCampusRepository.delete(campus); // 5. 清除当前学校校区缓存 String cacheKey = RedisConstants.SCHOOL_CAMPUS + "_" + user.getSchId(); redisUtil.del(cacheKey); LogContextHolder.setDescription("删除校区:"+campus.getName()); log.debug("操作人:{},删除校区日志:{}", user.getUserName(), JSON.toJSONString(campus)); } public List select(Integer schId) { List list = getSchDicCampusCacheList(schId); return ModelMapperUtil.toSelectIntVOList(list, SchDicCampus::getId, SchDicCampus::getName); } public Map toIdAndNameMap(Integer schId) { List list = getSchDicCampusCacheList(schId); return ModelMapperUtil.toMap(list, SchDicCampus::getId, SchDicCampus::getName); } }