班级同步

This commit is contained in:
2026-08-20 00:38:26 +08:00
parent 25b4d6ba1f
commit be754dc22c
8 changed files with 196 additions and 38 deletions
@@ -74,9 +74,9 @@ public class SchTreeController {
@GetMapping("getTeaTree")
public BoaGroup getTeaTree()
public BoaGroup getTeaTree(@RequestParam(required = false, defaultValue = "true") Boolean withSub)
{
return comSchTreeService.getTeaTree(UserContext.getSchId());
return comSchTreeService.getTeaTree(UserContext.getSchId(), withSub);
}
@@ -30,7 +30,11 @@ public interface IComSchTreeService {
List<SelectIntVO> getSchoolList();
BoaGroup getTeaTree(Integer schId);
BoaGroup getTeaTree(Integer schId, Boolean withSub);
BoaGroup getStuTree(Integer schId, Boolean withSub);
List<BoaGroup> getAcademicStageGroupList(Integer schId);
void validateGroupsBelongToSchool(Integer schId,List<Integer> groupIds);
@@ -43,4 +47,15 @@ public interface IComSchTreeService {
String getSchoolName(Integer schId);
Map<Integer,List<SelectIntVO>> getSchGroupTypeList(Integer schId, int type);
/**
* 清除全部缓存(Redis 组树/学校列表 + 本地内存缓存)
*/
void clearCache();
/**
* 清除指定学校的缓存(学校组树 + 全局缓存)
* @param schId 学校ID,为 null 时等同清除全部
*/
void clearCache(Integer schId);
}
@@ -16,9 +16,9 @@ import java.util.List;
@Service
public interface ISendUserBaseService {
BoaGroup addGroup(String name,Integer parentId,Integer sort, NodeType type);
BoaGroup addGroup(String name,Integer parentId,Integer sort, NodeType type,Integer schId);
void editGroup(Integer id,String name,Integer parentId,Integer sort, NodeType type);
void editGroup(Integer id,String name,Integer parentId,Integer sort, NodeType type,Integer schId);
BoaUser addTeacher(SchTeacher teacher, String pwd, Integer groupId);
@@ -243,11 +243,11 @@ public class SchBinSchsystemService implements ISchBinSchsystemService {
private void SchGroupInitialization(Integer schId,List<SchBinSchsystem> newList)
{
//添加学生组
BoaGroup teaGroup= sendUserBaseService.addGroup("教师组",schId,1,NodeType.TEACHER);
BoaGroup stuGroup= sendUserBaseService.addGroup("学生组",schId,2,NodeType.STUDENT);
BoaGroup teaGroup= sendUserBaseService.addGroup("教师组",schId,1,NodeType.TEACHER,schId);
BoaGroup stuGroup= sendUserBaseService.addGroup("学生组",schId,2,NodeType.STUDENT,schId);
//创建校长室
sendUserBaseService.addGroup("校长室", teaGroup.getId(),1,NodeType.NORMAL);
sendUserBaseService.addGroup("校长室", teaGroup.getId(),1,NodeType.NORMAL,schId);
//获取学制
@@ -261,7 +261,7 @@ public class SchBinSchsystemService implements ISchBinSchsystemService {
Integer scort=1;
for (SchBinSchsystem schsystem:newList) {
//添加学段组
BoaGroup sectionGroup= sendUserBaseService.addGroup(schsystemMap.get(schsystem.getSchSystemId()),stuGroup.getId(),scort,NodeType.SECTION);
BoaGroup sectionGroup= sendUserBaseService.addGroup(schsystemMap.get(schsystem.getSchSystemId()),stuGroup.getId(),scort,NodeType.SECTION,schId);
schsystem.setGroupId(sectionGroup.getId());
scort++;
}
@@ -244,7 +244,7 @@ public class SchTeacherService implements ISchTeacherService {
return Collections.emptyList();
}
BoaGroup teacherGroupTree = comSchTreeService.getTeaTree(schId);
BoaGroup teacherGroupTree = comSchTreeService.getTeaTree(schId,true);
if (teacherGroupTree == null) {
return Collections.emptyList();
}
@@ -2,13 +2,16 @@ package com.yunhai.bschclassmanage.service.impl.classes;
import com.yunhai.bschclassmanage.Repository.SchClassRepository;
import com.yunhai.bschclassmanage.entity.SchClass;
import com.yunhai.bschclassmanage.enums.AcademicStage;
import com.yunhai.bschclassmanage.enums.NodeType;
import com.yunhai.bschclassmanage.model.vo.SelectIntVO;
import com.yunhai.bschclassmanage.service.*;
import com.yunhai.bschclassmanage.utils.LogContextHolder;
import com.yunhai.bschclassmanage.utils.OtherUtil;
import com.yunhai.bschclassmanage.utils.UserContext;
import com.yunhai.platform.openapi.client.YschBaseOpenApi;
import com.yunhai.platform.utils.exception.DException;
import com.yunhai.platform.utils.model.BoaGroup;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -44,6 +47,9 @@ public class SchClassInfoService {
@Autowired
private SchClassQueryService queryService;
@Autowired
private ISendUserBaseService sendUserBaseService;
@Autowired
private SchClassLogService logService;
@@ -53,8 +59,33 @@ public class SchClassInfoService {
if (number == null || number <= 0) throw new DException("新增班级数量必须大于0");
Integer schId = user.getSchId();
BoaGroup stuGroup= comSchTreeService.getStuTree(schId,false);
if(stuGroup==null){
throw new DException("学生组不存在!");
}
Integer parentId=stuGroup.getId();
Integer schsystemId = baseSchsystemGradeService.getSchGradeIdBySchsystemId(schId, gradeId);
Map<Integer, String> schsystemMap = schBinSchsystemService.getSchBinSchsystemMap(schId);
if(schsystemMap.size()>1)
{
String academicStageName= schsystemMap.get(schsystemId);
List<BoaGroup> schAcademicStageList= comSchTreeService.getAcademicStageGroupList(schId);
Optional<BoaGroup> academicStageFirst= schAcademicStageList.stream().filter(s->Objects.equals(s.getName(),academicStageName)).findFirst();
if(!academicStageFirst.isPresent())
{
BoaGroup academicGroup= sendUserBaseService.addGroup(academicStageName,stuGroup.getId(),schsystemId,NodeType.SECTION,schId);
parentId=academicGroup.getId();
}else {
parentId=academicStageFirst.get().getId();
}
}
// 获取当前最大排序
Integer maxSort = schClassRepository.findMaxCodeNumber(gradeId, schId);
int startSort = (maxSort == null) ? 1 : maxSort + 1;
@@ -62,12 +93,21 @@ public class SchClassInfoService {
Map<Integer, Integer> nowYearGradeMap = baseSchsystemGradeService.getNowYearGradeMap(schId, schsystemId);
Integer year = nowYearGradeMap.get(gradeId);
//获取年级组
List<SelectIntVO> gradeGroupList = comSchTreeService.getSchGroupTypeList(schId, NodeType.GRADE.getId()).get(schsystemId);
if (gradeGroupList == null || gradeGroupList.isEmpty()) {
throw new DException("该学制下没有年级组,请先同步年级组数据!");
if (gradeGroupList != null) {
BoaGroup academicGroup= sendUserBaseService.addGroup(year.toString(),parentId,year,NodeType.GRADE,schId);
parentId=academicGroup.getId();
}else {
Optional<SelectIntVO> gradeGroupFirst=gradeGroupList.stream().filter(s->Objects.equals(year.toString(),s.getName())).findFirst();
if(!gradeGroupFirst.isPresent())
{
BoaGroup academicGroup= sendUserBaseService.addGroup(year.toString(),parentId,year,NodeType.GRADE,schId);
parentId=academicGroup.getId();
}else {
parentId=gradeGroupFirst.get().getId();
}
}
Map<String, Integer> gradeNameToIdMap = gradeGroupList.stream()
.collect(Collectors.toMap(SelectIntVO::getName, SelectIntVO::getId, (a, b) -> a));
List<SelectIntVO> classGroupList = comSchTreeService.getSchGroupTypeList(schId, NodeType.CLASS.getId()).get(schsystemId);
Map<String, Integer> classNameToIdMap = new HashMap<>();
@@ -87,18 +127,14 @@ public class SchClassInfoService {
clazz.setCode(code);
clazz.setName(sort + "班");
Integer groupId = gradeNameToIdMap.get(year.toString());
if (groupId == null) {
throw new DException("年级组 '" + year + "' 不存在,请检查年级组名称或同步树结构!");
}
clazz.setGroupId(groupId);
clazz.setGroupId(parentId);
Integer classId = classNameToIdMap.get(code);
if (classId == null) {
throw new DException("班级组 '" + code + "' 不存在,请检查班级组数据或同步树结构!");
BoaGroup academicGroup= sendUserBaseService.addGroup(code,parentId,sort,NodeType.CLASS,schId);
classId=academicGroup.getId();
}
clazz.setClassId(classId);
clazz.setGraduate(false);
clazz.setEnrollYear(year);
OtherUtil.setBaseEntityValue(user, clazz);
@@ -65,6 +65,32 @@ public class ComSchTreeService implements IComSchTreeService {
private Map<Integer, BaseSchsystem> baseSchsystemMap;
private final Object lock = new Object();
/**
* 清除全部缓存(Redis 组树/学校列表 + 本地内存缓存)
* 调用时机:组树、学校组织信息变更后
*/
public void clearCache() {
// 1. 清除 Redis 缓存
redisUtil.del(RedisConstants.SYSTEM_GROUP_TREE, RedisConstants.SCHOOL_LIST);
// 2. 清除本地内存缓存
synchronized (lock) {
baseSchsystemMap = null;
}
idToNameCache = null;
}
/**
* 清除指定学校的缓存(学校组树 + 全局缓存)
* @param schId 学校ID,为 null 时等同清除全部
*/
public void clearCache(Integer schId) {
if (schId != null) {
redisUtil.del(RedisConstants.SCHOOL_TREE + "_" + schId);
}
// 学校树变更可能影响学校列表/全局组树,一并清除
clearCache();
}
/**
* 获取基础学制映射(带双重检查锁)
*/
@@ -126,19 +152,21 @@ public class ComSchTreeService implements IComSchTreeService {
}
/**
* 获取 type=7 的节点及其所有子节点(教师组子树)
* 获取 type=7 的节点(教师组节点)
* @param schId 学校ID
* @return 教师组节点(含子节点),若未找到则返回 null
* @param withSub true=返回该节点及整棵子树;false=仅返回节点本身(不含子节点)
* @return 教师组节点,若未找到则返回 null
*/
public BoaGroup getTeaTree(Integer schId) {
public BoaGroup getTeaTree(Integer schId,Boolean withSub) {
List<BoaGroup> treeList = getSchTree(schId);
if (treeList == null || treeList.isEmpty()) {
return null;
}
// 遍历根节点列表(通常只有一个),递归查找 type=7
for (BoaGroup root : treeList) {
BoaGroup result = findNodeByType(root, NodeType.TEACHER.getId());
BoaGroup result = findNodeByType(root, NodeType.TEACHER.getId(),withSub);
if (result != null) {
return result;
}
@@ -146,6 +174,34 @@ public class ComSchTreeService implements IComSchTreeService {
return null;
}
public BoaGroup getStuTree(Integer schId,Boolean withSub) {
List<BoaGroup> treeList = getSchTree(schId);
if (treeList == null || treeList.isEmpty()) {
return null;
}
// 遍历根节点列表(通常只有一个),递归查找 type=7
for (BoaGroup root : treeList) {
BoaGroup result = findNodeByType(root, NodeType.STUDENT.getId(), withSub);
if (result != null) {
return result;
}
}
return null;
}
public List<BoaGroup> getAcademicStageGroupList(Integer schId)
{
List<BoaGroup> treeList = getSchTree(schId);
if (treeList == null || treeList.isEmpty()) {
return null;
}
List<BoaGroup> academicStageList = new ArrayList<>();
// 遍历根节点列表(通常只有一个),收集所有学段组(type=6)节点
for (BoaGroup root : treeList) {
collectGroupNodesByType(root, NodeType.SECTION.getId(), false, academicStageList);
}
return academicStageList;
}
@@ -161,7 +217,7 @@ public class ComSchTreeService implements IComSchTreeService {
}
// 方式一:通过教师树校验(如有缓存)
BoaGroup teaTree = getTeaTree(schId);
BoaGroup teaTree = getTeaTree(schId,true);
if (teaTree == null) {
throw new DException("该校暂无教师组");
}
@@ -178,9 +234,10 @@ public class ComSchTreeService implements IComSchTreeService {
}
public List<BoaGroup> getGroupTree() {
List<BoaGroup> treeList = getCacheSysGroupTree();
return getCacheSysGroupTree();
//List<BoaGroup> treeList = getCacheSysGroupTree();
// 过滤并处理:type=2的节点停止递归
return filterTreeByType(treeList, NodeType.SCHOOL.getId());
//return filterTreeByType(treeList, NodeType.SCHOOL.getId());
}
public List<BoaGroup> getCacheSysGroupTree() {
@@ -212,7 +269,7 @@ public class ComSchTreeService implements IComSchTreeService {
}
public List<BoaGroup> getSchTree(Integer schId) {
String cacheKey= RedisConstants.SYSTEM_GROUP_TREE+"_"+schId;
String cacheKey= RedisConstants.SCHOOL_TREE+"_"+schId;
List<BoaGroup> cached = redisUtil.getList(cacheKey, BoaGroup.class);
if (cached != null && !cached.isEmpty()) {
return cached;
@@ -303,7 +360,7 @@ public class ComSchTreeService implements IComSchTreeService {
public String getTeaGroupName(Integer schId, Integer id) {
BoaGroup tree = getTeaTree(schId);
BoaGroup tree = getTeaTree(schId,true);
if (tree == null) return null;
// 懒加载缓存(若树结构不变)
if (idToNameCache == null) {
@@ -317,7 +374,7 @@ public class ComSchTreeService implements IComSchTreeService {
public List<SelectIntVO> getTeaGroupNameList(Integer schId, List<Integer> ids) {
// 1. 构建缓存(如果尚未构建)
if (idToNameCache == null) {
BoaGroup tree = getTeaTree(schId);
BoaGroup tree = getTeaTree(schId,true);
if (tree == null) {
return Collections.emptyList();
}
@@ -367,7 +424,7 @@ public class ComSchTreeService implements IComSchTreeService {
*/
public BoaGroup getTeacherTree(Integer schId) {
// 1. 获取教师组织树(组织骨架)
BoaGroup teaTree = getTeaTree(schId);
BoaGroup teaTree = getTeaTree(schId,true);
if (teaTree == null) {
teaTree = new BoaGroup();
teaTree.setId(0);
@@ -598,18 +655,26 @@ public class ComSchTreeService implements IComSchTreeService {
}
/**
* 递归查找指定 type 的节点(返回该节点及子树)
* 递归查找指定 type 的节点
* @param node 当前节点
* @param targetType 目标类型
* @param withSub true=返回该节点及子树;false=仅返回节点本身(清空 children)
*/
private BoaGroup findNodeByType(BoaGroup node, int targetType) {
private BoaGroup findNodeByType(BoaGroup node, int targetType,Boolean withSub) {
if (node == null) {
return null;
}
if (node.getType() != null && node.getType() == targetType) {
return node; // 找到目标节点,返回整个节点(含其 children)
// withSub=false 时仅返回节点本身,不带子节点
//(getSchTree 每次从缓存反序列化出新对象,直接清空 children 不会污染缓存)
if (Boolean.FALSE.equals(withSub)) {
node.setChildren(new ArrayList<>());
}
return node; // 找到目标节点
}
if (node.getChildren() != null) {
for (BoaGroup child : node.getChildren()) {
BoaGroup found = findNodeByType(child, targetType);
BoaGroup found = findNodeByType(child, targetType,withSub);
if (found != null) {
return found;
}
@@ -618,6 +683,31 @@ public class ComSchTreeService implements IComSchTreeService {
return null;
}
/**
* 递归收集指定 type 的【所有】节点(与 findNodeByType 只返回第一个不同)
* @param node 当前节点
* @param targetType 目标类型
* @param withSub true=保留该节点的子节点;false=仅返回节点本身(清空 children)
* @param collector 结果收集器
*/
private void collectGroupNodesByType(BoaGroup node, int targetType, Boolean withSub, List<BoaGroup> collector) {
if (node == null) {
return;
}
if (node.getType() != null && node.getType() == targetType) {
// withSub=false 时仅返回节点本身,不带子节点
if (Boolean.FALSE.equals(withSub)) {
node.setChildren(new ArrayList<>());
}
collector.add(node);
}
if (node.getChildren() != null) {
for (BoaGroup child : node.getChildren()) {
collectGroupNodesByType(child, targetType, withSub, collector);
}
}
}
@@ -1162,4 +1252,7 @@ public class ComSchTreeService implements IComSchTreeService {
root.setChildren(gradeNodes);
return root;
}
}
@@ -6,6 +6,7 @@ import com.yunhai.bschclassmanage.enums.NodeType;
import com.yunhai.bschclassmanage.enums.UserRole;
import com.yunhai.bschclassmanage.annotation.SyncOpLog;
import com.yunhai.bschclassmanage.enums.SyncDataType;
import com.yunhai.bschclassmanage.service.IComSchTreeService;
import com.yunhai.bschclassmanage.service.ISendUserBaseService;
import com.yunhai.platform.openapi.client.YschBaseOpenApi;
import com.yunhai.platform.openapi.model.SysGroupRequest;
@@ -15,6 +16,7 @@ import com.yunhai.platform.utils.exception.DException;
import com.yunhai.platform.utils.model.BoaGroup;
import com.yunhai.platform.utils.model.BoaUser;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
@@ -25,8 +27,14 @@ import java.util.List;
@Service
public class SendUserBaseService implements ISendUserBaseService {
@Autowired
private IComSchTreeService comSchTreeService;
@SyncOpLog(SyncDataType.SEND_ADD_GROUP)
public BoaGroup addGroup(String name, Integer parentId, Integer sort, NodeType type){
public BoaGroup addGroup(String name, Integer parentId, Integer sort, NodeType type,Integer schId){
comSchTreeService.clearCache(schId);
SysGroupRequest group=new SysGroupRequest();
group.setName(name);
group.setParentId(parentId);
@@ -37,7 +45,10 @@ public class SendUserBaseService implements ISendUserBaseService {
@SyncOpLog(SyncDataType.SEND_EDIT_GROUP)
public void editGroup(Integer id,String name,Integer parentId,Integer sort, NodeType type){
public void editGroup(Integer id,String name,Integer parentId,Integer sort, NodeType type,Integer schId){
comSchTreeService.clearCache(schId);
SysGroupRequest group=new SysGroupRequest();
group.setName(name);
group.setParentId(parentId);
@@ -48,6 +59,9 @@ public class SendUserBaseService implements ISendUserBaseService {
@SyncOpLog(SyncDataType.SEND_DELETE_GROUP)
public void deleteGroup(Integer id,Integer schId){
comSchTreeService.clearCache(schId);
YschBaseOpenApi.deleteGroup(id,schId);
}