diff --git a/.workbuddy/memory/2026-08-14.md b/.workbuddy/memory/2026-08-14.md index 8f0b51d..376d8c3 100644 --- a/.workbuddy/memory/2026-08-14.md +++ b/.workbuddy/memory/2026-08-14.md @@ -1,83 +1,20 @@ # 2026-08-14 -## 独立 Bug 修复:`OtherUtil.jsonParseList` 泛型擦除导致 ClassCastException +## UserContext schId 超管置 0 +- 需求:全局设置 `UserContext.schId`,若用户 `TYPE=1`(超级管理员)则强制赋值 0(`UserType.SUPER_ADMINISTRATOR`)。 +- 改动文件:`src/main/java/com/yunhai/bschclassmanage/aspect/ControllerUserContextAspect.java` + - `populateUserContext()` 末尾在 `setType` 后增加判断:超管则 `UserContext.setSchId(0)`,覆盖 currentrole 带入的 schId。 + - 新增 import `com.yunhai.bschclassmanage.enums.UserType`。 +- 说明:该 Aspect(@Order(1),覆盖所有 controller)是实际填充 schId 的位置;UserContextInterceptor 的 resolveUser 仍返回 null,未设置 schId。未能本地编译(无 mvn、wrapper 缺 properties)。 -### 现象 -- `POST /oapi/user/AddUser`(同步用户接口,非 `api/` 包,未被 UserContext 重构涉及)返回 `code:6 / 内部服务错误`。 -- 服务器真实异常:`java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.yunhai.platform.utils.model.BoaUser` - 位置 `SyncUserService.syncAddOrUpdate(SyncUserService.java:54)`(增强 for 循环 `for (BoaUser user : userList)`)。 -- 连带次级错误(非致命、异步):审计日志 `sys_operation_log.request_params` 列存中文报 `Incorrect string value`(列字符集疑似 latin1/非 utf8mb4),被 `SysOperationLogServiceImpl` 捕获打印「异步落库失败」。 - -### 根因 -- `SyncUserController.AddUser` 用 `OtherUtil.jsonParseList(jsonString, BoaUser.class)` 解析成 `List`。 -- 原 `jsonParseList` 实现:`objectMapper.readValue(json, new TypeReference>() {})`。**Java 泛型擦除**使运行时 `List` 退化为原始 `List`,Jackson 把元素反序列化为 `LinkedHashMap`;遍历时强转 `BoaUser` 即 `ClassCastException`。 -- 同文件 `jsonParseArrayTree` 用 `objectMapper.getTypeFactory().constructCollectionType(List.class, elementClass)` 才正确——原 `jsonParseList` 注释写「TypeReference 保留泛型信息」是错的。 - -### 修复 -- `utils/OtherUtil.java` 的 `jsonParseList`:改用 `constructCollectionType(List.class, targetClass)` 构建 `JavaType` 再 `readValue`。 -- 影响面:顺带修好 `UpdateUser`、`DeleteUser`(同用 `jsonParseList`,之前同样会 ClassCast)。 - -### 待跟进(非本次阻塞) -- 审计日志表 `sys_operation_log.request_params` 列字符集问题:存中文报 `Incorrect string value`。需改 DDL 为 `utf8mb4`(或确认连接字符集)。属 DB schema,不影响主业务返回,已口头提示用户。 - -### 环境注意(延续) -- 本机 `mvn compile` 仍受 Lombok 命令行限制;改动在 IDE 验证。 - -## 深层根因 & 二次修复:`AddUser` 反序列化失败(JSON 与框架 BoaUser 结构不匹配) - -### 现象(修复 ClassCast 后仍未解决) -- `jsonParseList` 改 `constructCollectionType` 后,`AddUser` 仍 "反序列化失败"(500)。 -- 原因浮出:调用方下发的 JSON 是**自定义格式**,并非 yunhai 框架 `BoaUser`: - - `groups:[{"groupId":12,"no":null,"schoolId":3}]`、`roleList:[{"roleId":5,"schId":3}]` - - 原服务却按 `BoaUser` 调 `user.getSchool().getId()`、`group.getId()`、`group.getSort()`、`user.getRoleIds()` -- 用 `BoaUser.class` 反序列化时,Jackson(默认 `FAIL_ON_UNKNOWN_PROPERTIES=true`)对未知字段 `groups`/`roleList` 抛 `UnrecognizedPropertyException` → 仍 500。 - -### 修复(按用户指示:加 DTO 补字段) -- 新增 3 个 DTO(`model/tool` 包,与 `DeleteUser` 同风格、`@Data` + `@JsonIgnoreProperties(ignoreUnknown=true)`): - - `SyncUserDTO`:id, account, name, idcard, phone, email, gender(Integer), birthday, status(Integer), profilePicture, `List groups`, `List roleList` - - `SyncGroupDTO`:groupId, no(Integer), schoolId - - `SyncRoleDTO`:roleId, schId -- `SyncUserController`:`jsonParseList(jsonString, SyncUserDTO.class)`;`ISyncUserService.syncAddOrUpdate(List)`。 -- 重写 `SyncUserService`:去掉 `BoaUser`/`BoaGroup` 依赖,按真实字段映射: - - 角色判定改用 `user.getRoleList().stream().map(SyncRoleDTO::getRoleId)` 比 `UserRole` 值; - - `group.getId()`→`group.getGroupId()`;`user.getSchool().getId()`→`group.getSchoolId()`;`group.getSort()`→`group.getNo()`;`stuClassNo` = `String.valueOf(group.getNo())`。 -- `SchStudent.schId` 继承自 `BaseEntity`(存在);实体字段类型与 DTO 对齐(gender/status 为 Integer,idcard/account/name/phone 为 String)。 - -### 结论 -- 该接口此前(含重构前)从未真正可用:先因 `LinkedHashMap` 强转炸,后为 `BoaUser` 字段不匹配炸。现在用匹配 JSON 的 DTO 彻底解耦。 -- 注意 JSON 中 `roleId=5`(BUREAU_LEADER_USER),在当前样本数据下既不命中 isStudent 也不命中 isTeacher,故不会落库;这是调用方业务数据问题,非代码缺陷。 - -## 优化 `SyncUserService` -- 重写 `service/impl/sync/SyncUserService.java`,优化点: - 1. 角色判定改为每个用户只遍历一次 `roleList`(`collectRoleIds` 提取去重 Set),`isStudent`/`isTeacher` 改判集合包含,去除重复流式遍历。 - 2. `deleteUser` 中 `studentDeletes`/`teacherDeletes` 同一过滤条件算了两次 → 合并为单次 `validDeletes`;删除死代码 `groupsBySchool`。 - 3. 修复教师分组删除 `toMap` 2 段键在「同教师同 groupId 跨多学校」时被静默丢键的 bug → 改用 `Collectors.groupingBy` 收集 `List`,删除全部匹配分组。 - 4. 缓存清除去重:delete 时按 (schId,classId)/(schId) 去重后调用 `clearClassStudentCaches`/`clearTeacherCaches`(新增的批量方法)。 - 5. 补齐 add/update 后的缓存失效:此前只在 delete 清缓存,add/update 后班级学生缓存/学校教师缓存会陈旧 → 现同步保存后收集受影响键并 `clearClassStudentCaches`/`clearTeacherCaches`。 -- 行为保持:事务注解、角色归类(学生优先于教师)、字段映射不变。 - -## 优化 `SchClassUpgradeService`(学年升级) -- `upgradeSingleSystem` 原本每个年级各调一次 `queryService.getSchClassCacheFilter` 反序列化全校班级列表、再逐年级 `saveAll`;改为**基于 SET 的批量 `@Modifying` 更新**: - - 新增 `SchClassRepository.batchUpgradeGrade(schId, 非最高年级列表, now)`:`grade_id = grade_id + 1` 单语句原子完成(仅基础教育)。 - - 新增 `SchClassRepository.batchGraduate(schId, maxGrade, now)`:最高年级 `graduate = true`。 - - 用 `?1 ?2 ?3` 位置参数避免受现有 lettuce `@Param` 误用影响;`clearAutomatically = true` 与 `SchStudentRepository` 风格一致。 -- `upgradeAllClasses` 优化/修复: - - `schType` 提升到学制循环外只查一次(原在学制内重复查)。 - - `getCurrentYearTerm()` 加 null 守卫,避免 NPE。 - - 原 `schType.equals(...)` 在 `getSchType` 返回 null 时 NPE → 改为常量 `.equals(schType)` 判等,null 安全。 - - 升级日志保存包 try/catch,避免日志写入失败掩盖升级结果。 - - 升级后除 `clearClassCache` 外补 `clearGradeateClassCache`,消除毕业后毕业缓存脏数据(原漏清)。 -- 行为保持:仅基础教育非最高年级升级、最高年级毕业;失败不阻断其他学制;失败记录可重试(existsBy...Status=true 不命中)。 - -## 全局修正 Repository 的 `@Param` 错误注解 -- 发现 7 个 Repository 的 `@Query` 命名参数绑定误用了 `io.lettuce.core.dynamic.annotation.Param`(Lettuce Redis 注解),而非 Spring Data 的 `org.springframework.data.repository.query.Param`;后者不被 Spring Data 识别,运行时触发「Parameter binding not found」,属潜在崩溃隐患。 -- 统一替换为 `org.springframework.data.repository.query.Param`:SchClassRepository、SchClassHistoryRepository、SchDicCourseRepository、SchTeacherRepository、SchDicMajorsRepository、SchStudentRepository、SchTeacherGroupRepository。 -- 顺带在 SchClassRepository 补 `import java.util.Date;`(批量升级方法用到)并恢复 `Page`/`Pageable` 导入。 -- 现状:本沙箱无 Maven 无法编译验证,需本机 `mvn compile` 确认。 - -## 修复 `SchClassUpgradeService` 中 `JdbcTemplate` 报红 -- 现象:IDE 中标红 `private JdbcTemplate jdbcTemplate;`。 -- 根因:`pom.xml` 仅引入 `spring-boot-starter-data-jpa`(其传递依赖 `spring-boot-starter-jdbc`/`spring-jdbc` 含 `JdbcTemplate`),但 IDE 未正确解析传递依赖时 `JdbcTemplate` 类找不到 → 报红。 -- 修复: - 1. 在 `pom.xml` 显式新增 `spring-boot-starter-jdbc` 依赖(置于 data-jpa 之后),保证 `JdbcTemplate` 类与自动配置 Bean 稳定可用。 - 2. `SchClassUpgradeService` 由字段注入改为 Lombok `@RequiredArgsConstructor` 构造器注入,消除 IDEA 「Could not autowire. No beans of 'JdbcTemplate' type found」的自动注入识别告警,同时符合 Spring 推荐写法。`backupAllSnapshot` 的批量 INSERT...SELECT 快照用法保留。 +## ComSchTreeService:BaseTree → BoaGroup 迁移 +- 需求:将 `ComSchTreeService` 中树模型由 `com.yunhai.bschclassmanage.model.tool.BaseTree` 换成平台 `com.yunhai.platform.utils.model.BoaGroup`。 +- 改动文件:`service/com/ComSchTreeService.java`、`service/IComSchTreeService.java`、`controller/api/SchTreeController.java`、`service/impl/SchTeacherService.java`(toTeacherGroupSelect + 私有 flattenTree)。 +- 字段映射要点: + - BoaGroup 无 `expand`/`code`/`order`;有 `parentId`/`sort`(Integer)。 + - 删除教师树节点上的 `setExpand`/`setCode`;`setOrder(String)` → `setSort(teacher.getSort())`;`sortTreeNodes` 排序改为按 `getSort()`(Integer, nullsLast)。 + - `SchSystemTreeVO` 的 `setExpand`/`setSort` 保持不变(那是年级/班级树,另一套模型)。 + - 接口 `getGroupTree()` 原声明为 `BoaGroup`(与实现 `List` 不匹配),改为 `List`。 + - 红缓存 `SCHOOL_TREE_{schId}` 读取端由 BaseTree 改为 BoaGroup 解析;写入端未在本次仓库内找到(疑似外部/平台写入),解析向后兼容(缺字段为 null,多余字段忽略)。 +- 注意:`BaseTree.java` 现已无引用,可删除(本次未删)。`expand` 字段消失会影响前端教师树自动展开,需前端确认。 +- 未能本地编译(无 JDK)。 diff --git a/src/main/java/com/yunhai/bschclassmanage/Repository/specification/OperationLogSpecifications.java b/src/main/java/com/yunhai/bschclassmanage/Repository/specification/OperationLogSpecifications.java index b3d7f8c..7190255 100644 --- a/src/main/java/com/yunhai/bschclassmanage/Repository/specification/OperationLogSpecifications.java +++ b/src/main/java/com/yunhai/bschclassmanage/Repository/specification/OperationLogSpecifications.java @@ -10,7 +10,7 @@ public class OperationLogSpecifications { public static Specification schIdEquals(Integer schId) { return (root, query, cb) -> { - if (schId == null || schId <= 0) { + if (schId == null || schId < 0) { return cb.conjunction(); } return cb.equal(root.get("schId"), schId); diff --git a/src/main/java/com/yunhai/bschclassmanage/aspect/ControllerUserContextAspect.java b/src/main/java/com/yunhai/bschclassmanage/aspect/ControllerUserContextAspect.java index 610e7ce..d46575d 100644 --- a/src/main/java/com/yunhai/bschclassmanage/aspect/ControllerUserContextAspect.java +++ b/src/main/java/com/yunhai/bschclassmanage/aspect/ControllerUserContextAspect.java @@ -1,6 +1,7 @@ package com.yunhai.bschclassmanage.aspect; import com.fasterxml.jackson.databind.ObjectMapper; +import com.yunhai.bschclassmanage.enums.UserType; import com.yunhai.bschclassmanage.model.tool.UserSysRole; import com.yunhai.bschclassmanage.utils.OtherUtil; import com.yunhai.bschclassmanage.utils.UserContext; @@ -143,6 +144,13 @@ public class ControllerUserContextAspect { UserContext.setType(boaUser != null ? boaUser.getType() : null); + // 超级管理员(TYPE = 1)不绑定具体学校,schId 统一置 0, + // 覆盖 currentrole 中可能带入的学校ID,保证数据隔离逻辑对超管一致识别 + if (UserType.SUPER_ADMINISTRATOR.getId().equals(UserContext.getType())) { + UserContext.setSchId(0); + log.debug("Super admin detected(TYPE=1), force schId = 0"); + } + String clientIp = OtherUtil.getClientIp(); UserContext.setIp(clientIp); log.debug("Client IP set: {}", clientIp); diff --git a/src/main/java/com/yunhai/bschclassmanage/controller/api/SchTreeController.java b/src/main/java/com/yunhai/bschclassmanage/controller/api/SchTreeController.java index 2c71286..c28a3b9 100644 --- a/src/main/java/com/yunhai/bschclassmanage/controller/api/SchTreeController.java +++ b/src/main/java/com/yunhai/bschclassmanage/controller/api/SchTreeController.java @@ -3,8 +3,8 @@ import com.yunhai.bschclassmanage.utils.UserContext; import com.yunhai.bschclassmanage.config.Config; -import com.yunhai.bschclassmanage.model.tool.BaseTree; import com.yunhai.bschclassmanage.model.vo.SchSystemTreeVO; +import com.yunhai.platform.utils.model.BoaGroup; import com.yunhai.bschclassmanage.model.vo.SelectIntVO; import com.yunhai.bschclassmanage.service.IComSchTreeService; import org.springframework.beans.factory.annotation.Autowired; @@ -23,13 +23,13 @@ public class SchTreeController { @GetMapping("getGroupTree") - public List getGroupTree() + public List getGroupTree() { return comSchTreeService.getGroupTree(); } @GetMapping("getTeacherTree") - public BaseTree getTeacherTree() + public BoaGroup getTeacherTree() { return comSchTreeService.getTeacherTree(UserContext.getSchId()); } @@ -74,7 +74,7 @@ public class SchTreeController { @GetMapping("getTeaTree") - public BaseTree getTeaTree() + public BoaGroup getTeaTree() { return comSchTreeService.getTeaTree(UserContext.getSchId()); } diff --git a/src/main/java/com/yunhai/bschclassmanage/controller/api/SchsystemController.java b/src/main/java/com/yunhai/bschclassmanage/controller/api/SchsystemController.java index e5dcf28..0522be4 100644 --- a/src/main/java/com/yunhai/bschclassmanage/controller/api/SchsystemController.java +++ b/src/main/java/com/yunhai/bschclassmanage/controller/api/SchsystemController.java @@ -55,13 +55,12 @@ public class SchsystemController { throw new DException("当前学校管理员未关联有效的学校"); } } else { // 超管 - if (schId == null || schId <= 0) { + if (schId == null || schId < 0) { throw new DException("学校ID不能为空且必须为正整数"); } targetSchId = schId; } - return schBinSchsystemService.getAcademicStageSchsystemList(targetSchId); } diff --git a/src/main/java/com/yunhai/bschclassmanage/controller/oapi/sync/SyncUserController.java b/src/main/java/com/yunhai/bschclassmanage/controller/oapi/sync/SyncUserController.java index 61fac2a..7b8b474 100644 --- a/src/main/java/com/yunhai/bschclassmanage/controller/oapi/sync/SyncUserController.java +++ b/src/main/java/com/yunhai/bschclassmanage/controller/oapi/sync/SyncUserController.java @@ -21,27 +21,27 @@ public class SyncUserController { @PostMapping("AddUser") public void AddUser(@RequestBody String jsonString) { - log.info("jsonString="+jsonString); + log.info("AddUser jsonString="+jsonString); List userlist = OtherUtil.jsonParseList(jsonString, SyncUserDTO.class); syncUserService.syncAddOrUpdate(userlist); } @PostMapping("UpdateUser") public void UpdateUser(@RequestBody String jsonString) { - log.info("jsonString="+jsonString); + log.info("UpdateUser jsonString="+jsonString); List userList = OtherUtil.jsonParseList(jsonString, SyncUserDTO.class); syncUserService.syncAddOrUpdate(userList); } @PostMapping("DeleteUser") public void DeleteUser(@RequestBody String jsonString) { - log.info("jsonString="+jsonString); + log.info("DeleteUser jsonString="+jsonString); List deleteUserList= OtherUtil.jsonParseList(jsonString, DeleteUser.class); syncUserService.deleteUser(deleteUserList); } @PostMapping("SyncUser") public void SyncUser(@RequestBody String jsonString) { - log.info("jsonString="+jsonString); + log.info("SyncUser jsonString="+jsonString); } } diff --git a/src/main/java/com/yunhai/bschclassmanage/enums/UserType.java b/src/main/java/com/yunhai/bschclassmanage/enums/UserType.java index 779b294..965998a 100644 --- a/src/main/java/com/yunhai/bschclassmanage/enums/UserType.java +++ b/src/main/java/com/yunhai/bschclassmanage/enums/UserType.java @@ -12,7 +12,8 @@ public enum UserType //状态 0禁用 1 正常 2休学 3出国 4留学 5复学 6停学 7退学 8留级 9其他 SUPER_ADMINISTRATOR(1,"超级管理员"), - REGULAR_USER(2,"普通用户"); + SCHOOL_ADMIN(2,"学校管理员"), + REGULAR_USER(3,"普通用户"); private Integer id; private String name; diff --git a/src/main/java/com/yunhai/bschclassmanage/service/IComSchTreeService.java b/src/main/java/com/yunhai/bschclassmanage/service/IComSchTreeService.java index 79629fc..4faa876 100644 --- a/src/main/java/com/yunhai/bschclassmanage/service/IComSchTreeService.java +++ b/src/main/java/com/yunhai/bschclassmanage/service/IComSchTreeService.java @@ -1,10 +1,10 @@ package com.yunhai.bschclassmanage.service; import com.yunhai.bschclassmanage.entity.BaseDicGrade; -import com.yunhai.bschclassmanage.model.tool.BaseTree; import com.yunhai.bschclassmanage.model.vo.SchSystemTreeVO; import com.yunhai.bschclassmanage.model.vo.SelectIntVO; import com.yunhai.bschclassmanage.model.vo.SelectStrVO; +import com.yunhai.platform.utils.model.BoaGroup; import org.springframework.stereotype.Service; import java.util.List; @@ -24,13 +24,13 @@ public interface IComSchTreeService { SchSystemTreeVO getHigherSchClassTree(Integer schId); - List getSchTree(Integer schId); + List getSchTree(Integer schId); - BaseTree getTeacherTree(Integer schId); + BoaGroup getTeacherTree(Integer schId); List getSchoolList(); - BaseTree getTeaTree(Integer schId); + BoaGroup getTeaTree(Integer schId); void validateGroupsBelongToSchool(Integer schId,List groupIds); @@ -38,7 +38,7 @@ public interface IComSchTreeService { List getTeaGroupNameList(Integer schId, List ids); - List getGroupTree(); + List getGroupTree(); String getSchoolName(Integer schId); diff --git a/src/main/java/com/yunhai/bschclassmanage/service/com/ComSchTreeService.java b/src/main/java/com/yunhai/bschclassmanage/service/com/ComSchTreeService.java index 5e703c4..eb5a3cb 100644 --- a/src/main/java/com/yunhai/bschclassmanage/service/com/ComSchTreeService.java +++ b/src/main/java/com/yunhai/bschclassmanage/service/com/ComSchTreeService.java @@ -9,7 +9,6 @@ import com.yunhai.bschclassmanage.entity.*; import com.yunhai.bschclassmanage.enums.AcademicStage; import com.yunhai.bschclassmanage.enums.NodeType; import com.yunhai.bschclassmanage.enums.SchType; -import com.yunhai.bschclassmanage.model.tool.BaseTree; import com.yunhai.bschclassmanage.model.vo.SchSystemTreeVO; import com.yunhai.bschclassmanage.model.vo.SelectIntVO; import com.yunhai.bschclassmanage.model.vo.SelectStrVO; @@ -17,13 +16,16 @@ import com.yunhai.bschclassmanage.service.*; import com.yunhai.bschclassmanage.utils.OtherUtil; import com.yunhai.bschclassmanage.utils.RedisConstants; import com.yunhai.bschclassmanage.utils.RedisUtil; +import com.yunhai.platform.openapi.client.YschBaseOpenApi; import com.yunhai.platform.utils.exception.DException; +import com.yunhai.platform.utils.model.BoaGroup; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; +import java.sql.Array; import java.util.*; import java.util.stream.Collectors; @@ -104,10 +106,10 @@ public class ComSchTreeService implements IComSchTreeService { } - public List getSchTree(Integer schId) { + public List getSchTree(Integer schId) { String cacheKey = RedisConstants.SCHOOL_TREE + "_" + schId; String json = redisUtil.get(cacheKey,String.class); - List treeList = OtherUtil.jsonParseArrayTree(json, BaseTree.class); + List treeList = OtherUtil.jsonParseArrayTree(json, BoaGroup.class); return treeList; } @@ -131,15 +133,14 @@ public class ComSchTreeService implements IComSchTreeService { * @param schId 学校ID * @return 教师组节点(含子节点),若未找到则返回 null */ - public BaseTree getTeaTree(Integer schId) { - List treeList = getSchTree(schId); + public BoaGroup getTeaTree(Integer schId) { + List treeList = getSchTree(schId); if (treeList == null || treeList.isEmpty()) { return null; } // 遍历根节点列表(通常只有一个),递归查找 type=7 - for (BaseTree root : treeList) { - root.setExpand(true); - BaseTree result = findNodeByType(root, NodeType.TEACHER.getId()); + for (BoaGroup root : treeList) { + BoaGroup result = findNodeByType(root, NodeType.TEACHER.getId()); if (result != null) { return result; } @@ -162,13 +163,13 @@ public class ComSchTreeService implements IComSchTreeService { } // 方式一:通过教师树校验(如有缓存) - BaseTree teaTree = getTeaTree(schId); + BoaGroup teaTree = getTeaTree(schId); if (teaTree == null) { throw new DException("该校暂无教师组"); } - List allNodes = flattenTree(teaTree); + List allNodes = flattenTree(teaTree); Set validGroupIds = allNodes.stream() - .map(BaseTree::getId) + .map(BoaGroup::getId) .collect(Collectors.toSet()); for (Integer groupId : groupIds) { @@ -178,14 +179,22 @@ public class ComSchTreeService implements IComSchTreeService { } } - public List getGroupTree() { - String cacheKey = "bsch.classmanage.group_tree"; - // 直接从Redis获取JSON字符串 - String json = redisUtil.get(cacheKey, String.class); - // 解析为树结构 - List treeList = OtherUtil.jsonParseArrayTree(json, BaseTree.class); - // 过滤并处理:type=2的节点停止递归 - return filterTreeByType(treeList, NodeType.SCHOOL.getId()); + // public List getGroupTree() { + + // return YschBaseOpenApi.getGroupTree(); + +// String cacheKey = "bsch.classmanage.group_tree"; +// // 直接从Redis获取JSON字符串 +// String json = redisUtil.get(cacheKey, String.class); +// // 解析为树结构 +// List treeList = OtherUtil.jsonParseArrayTree(json, BoaGroup.class); +// // 过滤并处理:type=2的节点停止递归 +// return filterTreeByType(treeList, NodeType.SCHOOL.getId()); + //} + + public List getGroupTree() { + BoaGroup group = YschBaseOpenApi.getGroupTree(); + return Collections.singletonList(group); } public List getSchoolList() { @@ -197,14 +206,14 @@ public class ComSchTreeService implements IComSchTreeService { } // 2. 从组树构建(扁平化收集所有学校节点) - List treeList = getGroupTree(); + List treeList = getGroupTree(); if (treeList == null || treeList.isEmpty()) { return Collections.emptyList(); } // 收集所有学校节点 - List schoolNodes = new ArrayList<>(); - for (BaseTree root : treeList) { + List schoolNodes = new ArrayList<>(); + for (BoaGroup root : treeList) { flattenTreeByType(root, NodeType.SCHOOL.getId(), schoolNodes); } @@ -227,7 +236,7 @@ public class ComSchTreeService implements IComSchTreeService { public Map> getSchGroupTypeList(Integer schId, int type) { Map academicStageToSchsystemMap = schBinSchsystemService.getAcademicStageToSchsystemMap(schId); Map> resultMap = new HashMap<>(); - List schTree = getSchTree(schId); + List schTree = getSchTree(schId); if (schTree == null || schTree.isEmpty()) { return resultMap; } @@ -252,7 +261,7 @@ public class ComSchTreeService implements IComSchTreeService { public String getTeaGroupName(Integer schId, Integer id) { - BaseTree tree = getTeaTree(schId); + BoaGroup tree = getTeaTree(schId); if (tree == null) return null; // 懒加载缓存(若树结构不变) if (idToNameCache == null) { @@ -266,7 +275,7 @@ public class ComSchTreeService implements IComSchTreeService { public List getTeaGroupNameList(Integer schId, List ids) { // 1. 构建缓存(如果尚未构建) if (idToNameCache == null) { - BaseTree tree = getTeaTree(schId); + BoaGroup tree = getTeaTree(schId); if (tree == null) { return Collections.emptyList(); } @@ -314,16 +323,15 @@ public class ComSchTreeService implements IComSchTreeService { * - 组织节点 type=0,children 为子节点列表(保证非 null) * - 教师节点 type=1,children 为空列表([]) */ - public BaseTree getTeacherTree(Integer schId) { + public BoaGroup getTeacherTree(Integer schId) { // 1. 获取教师组织树(组织骨架) - BaseTree teaTree = getTeaTree(schId); + BoaGroup teaTree = getTeaTree(schId); if (teaTree == null) { - teaTree = new BaseTree(); + teaTree = new BoaGroup(); teaTree.setId(0); teaTree.setName("教师组"); teaTree.setType(0); teaTree.setChildren(new ArrayList<>()); - teaTree.setExpand(true); } // 2. 获取该校所有教师 @@ -350,18 +358,18 @@ public class ComSchTreeService implements IComSchTreeService { fixTreeTypes(teaTree, teacherIdSet, orgIdSet); // 5. 构建组织ID -> 节点映射 - Map orgNodeMap = new HashMap<>(); + Map orgNodeMap = new HashMap<>(); buildOrgNodeMap(teaTree, orgNodeMap); // 6. 遍历教师,添加到对应组织 for (SchTeacher teacher : teacherList) { List groupIds = teaIdToGroupIds.getOrDefault(teacher.getTeaId(), Collections.emptyList()); if (groupIds.isEmpty()) { - BaseTree defaultGroup = getOrCreateDefaultGroup(teaTree, orgNodeMap); + BoaGroup defaultGroup = getOrCreateDefaultGroup(teaTree, orgNodeMap); addTeacherToNode(defaultGroup, teacher); } else { for (Integer groupId : groupIds) { - BaseTree parentNode = orgNodeMap.get(groupId); + BoaGroup parentNode = orgNodeMap.get(groupId); if (parentNode == null) { parentNode = getOrCreateDefaultGroup(teaTree, orgNodeMap); } @@ -382,13 +390,13 @@ public class ComSchTreeService implements IComSchTreeService { /** * 递归确保所有节点的 children 不为 null,若为 null 则设置为空列表 [] */ - private void ensureChildrenNotEmpty(BaseTree node) { + private void ensureChildrenNotEmpty(BoaGroup node) { if (node == null) return; if (node.getChildren() == null) { node.setChildren(new ArrayList<>()); } if (node.getChildren() != null) { - for (BaseTree child : node.getChildren()) { + for (BoaGroup child : node.getChildren()) { ensureChildrenNotEmpty(child); } } @@ -401,7 +409,7 @@ public class ComSchTreeService implements IComSchTreeService { * - 如果节点ID在 orgIdSet 中,设为 type=0 * - 其他情况:如果有 children(非空且非空列表)则设为 type=0,否则设为 type=0(默认为组织) */ - private void fixTreeTypes(BaseTree node, Set teacherIdSet, Set orgIdSet) { + private void fixTreeTypes(BoaGroup node, Set teacherIdSet, Set orgIdSet) { if (node == null) return; // 根节点(id=0 或 其他)设为组织 if (node.getId().equals(0) || node.getType() == 7) { @@ -420,7 +428,7 @@ public class ComSchTreeService implements IComSchTreeService { } // 递归处理子节点 if (node.getChildren() != null) { - for (BaseTree child : node.getChildren()) { + for (BoaGroup child : node.getChildren()) { fixTreeTypes(child, teacherIdSet, orgIdSet); } } @@ -430,19 +438,17 @@ public class ComSchTreeService implements IComSchTreeService { * 将教师添加到指定组织节点(去重) * 教师节点的 children 设置为空列表([]),而非 null */ - private void addTeacherToNode(BaseTree parentNode, SchTeacher teacher) { + private void addTeacherToNode(BoaGroup parentNode, SchTeacher teacher) { if (parentNode == null) return; // 检查是否已存在该教师(按 teaId 去重) if (isTeacherExists(parentNode, teacher.getTeaId())) { return; } - BaseTree teacherNode = new BaseTree(); + BoaGroup teacherNode = new BoaGroup(); teacherNode.setId(teacher.getTeaId()); teacherNode.setName(teacher.getName()); teacherNode.setType(1); - teacherNode.setCode(null); - teacherNode.setOrder(teacher.getSort() != null ? String.valueOf(teacher.getSort()) : null); - teacherNode.setExpand(false); + teacherNode.setSort(teacher.getSort()); // ★ 修改点:教师节点的 children 设为空列表,而不是 null teacherNode.setChildren(new ArrayList<>()); @@ -455,13 +461,13 @@ public class ComSchTreeService implements IComSchTreeService { /** * 递归构建组织 ID -> 节点映射(仅 type=0) */ - private void buildOrgNodeMap(BaseTree node, Map orgNodeMap) { + private void buildOrgNodeMap(BoaGroup node, Map orgNodeMap) { if (node == null) return; if (node.getType() == 0) { orgNodeMap.put(node.getId(), node); } if (node.getChildren() != null) { - for (BaseTree child : node.getChildren()) { + for (BoaGroup child : node.getChildren()) { buildOrgNodeMap(child, orgNodeMap); } } @@ -470,16 +476,15 @@ public class ComSchTreeService implements IComSchTreeService { /** * 获取或创建“未分组”节点(ID = -1) */ - private BaseTree getOrCreateDefaultGroup(BaseTree root, Map orgNodeMap) { + private BoaGroup getOrCreateDefaultGroup(BoaGroup root, Map orgNodeMap) { final Integer DEFAULT_GROUP_ID = -1; - BaseTree defaultGroup = orgNodeMap.get(DEFAULT_GROUP_ID); + BoaGroup defaultGroup = orgNodeMap.get(DEFAULT_GROUP_ID); if (defaultGroup == null) { - defaultGroup = new BaseTree(); + defaultGroup = new BoaGroup(); defaultGroup.setId(DEFAULT_GROUP_ID); defaultGroup.setName("未分组"); defaultGroup.setType(0); defaultGroup.setChildren(new ArrayList<>()); - defaultGroup.setExpand(true); if (root.getChildren() == null) { root.setChildren(new ArrayList<>()); } @@ -492,9 +497,9 @@ public class ComSchTreeService implements IComSchTreeService { /** * 检查节点下是否已存在指定 teaId 的教师 */ - private boolean isTeacherExists(BaseTree parentNode, Integer teaId) { + private boolean isTeacherExists(BoaGroup parentNode, Integer teaId) { if (parentNode.getChildren() == null) return false; - for (BaseTree child : parentNode.getChildren()) { + for (BoaGroup child : parentNode.getChildren()) { if (child.getType() == 1 && child.getId().equals(teaId)) { return true; } @@ -506,36 +511,26 @@ public class ComSchTreeService implements IComSchTreeService { * 排序:先按 order 排(若不为空),再按 name 排序(组织节点和教师节点分别处理) * 这里只对教师节点排序,组织节点保留原顺序 */ - private void sortTreeNodes(BaseTree node) { + private void sortTreeNodes(BoaGroup node) { if (node == null || node.getChildren() == null) return; // 分离组织节点和教师节点 - List orgChildren = node.getChildren().stream() + List orgChildren = node.getChildren().stream() .filter(c -> c.getType() == 0) .collect(Collectors.toList()); - List teacherChildren = node.getChildren().stream() + List teacherChildren = node.getChildren().stream() .filter(c -> c.getType() == 1) - .sorted(Comparator.comparing((BaseTree t) -> { - String order = t.getOrder(); - if (order != null && !order.isEmpty()) { - try { - return Integer.parseInt(order); - } catch (NumberFormatException e) { - return Integer.MAX_VALUE; - } - } - return Integer.MAX_VALUE; - }) - .thenComparing(BaseTree::getName, String.CASE_INSENSITIVE_ORDER)) + .sorted(Comparator.comparing((BoaGroup t) -> t.getSort(), Comparator.nullsLast(Integer::compareTo)) + .thenComparing(BoaGroup::getName, String.CASE_INSENSITIVE_ORDER)) .collect(Collectors.toList()); // 合并:先组织,后教师 - List sorted = new ArrayList<>(); + List sorted = new ArrayList<>(); sorted.addAll(orgChildren); sorted.addAll(teacherChildren); node.setChildren(sorted); // 递归排序子组织 - for (BaseTree child : orgChildren) { + for (BoaGroup child : orgChildren) { sortTreeNodes(child); } } @@ -563,7 +558,7 @@ public class ComSchTreeService implements IComSchTreeService { /** * 递归查找指定 type 的节点(返回该节点及子树) */ - private BaseTree findNodeByType(BaseTree node, int targetType) { + private BoaGroup findNodeByType(BoaGroup node, int targetType) { if (node == null) { return null; } @@ -571,8 +566,8 @@ public class ComSchTreeService implements IComSchTreeService { return node; // 找到目标节点,返回整个节点(含其 children) } if (node.getChildren() != null) { - for (BaseTree child : node.getChildren()) { - BaseTree found = findNodeByType(child, targetType); + for (BoaGroup child : node.getChildren()) { + BoaGroup found = findNodeByType(child, targetType); if (found != null) { return found; } @@ -704,14 +699,14 @@ public class ComSchTreeService implements IComSchTreeService { * @param stopType 停止递归的类型 * @return 过滤后的树列表 */ - private List filterTreeByType(List treeList, int stopType) { + private List filterTreeByType(List treeList, int stopType) { if (treeList == null || treeList.isEmpty()) { return treeList; } - List result = new ArrayList<>(); + List result = new ArrayList<>(); - for (BaseTree node : treeList) { + for (BoaGroup node : treeList) { // 如果当前节点type等于stopType,保留该节点但清空子节点 if (node.getType() == stopType) { // 保留该节点,但移除所有子节点(停止递归) @@ -720,7 +715,7 @@ public class ComSchTreeService implements IComSchTreeService { } else { // type不等于stopType,递归处理子节点 if (node.getChildren() != null && !node.getChildren().isEmpty()) { - List filteredChildren = filterTreeByType(node.getChildren(), stopType); + List filteredChildren = filterTreeByType(node.getChildren(), stopType); node.setChildren(filteredChildren); } result.add(node); @@ -795,9 +790,9 @@ public class ComSchTreeService implements IComSchTreeService { * 原有递归收集方法(单学段或无条件收集) * 不进行分组,将所有符合类型的节点加入 result 列表 */ - private void collectNodesByType(List treeList, int targetType, + private void collectNodesByType(List treeList, int targetType, List result) { - for (BaseTree node : treeList) { + for (BoaGroup node : treeList) { if (node.getType() == targetType) { SelectIntVO vo = new SelectIntVO(); vo.setId(node.getId()); @@ -819,12 +814,12 @@ public class ComSchTreeService implements IComSchTreeService { * @param stageToSchsystemMap 学段ID→学制ID映射 * @param currentStageId 当前路径上最近匹配的学段ID(null表示尚未匹配) */ - private void collectNodesGroupByStage(List treeList, int targetType, + private void collectNodesGroupByStage(List treeList, int targetType, Map> resultMap, Map stageNameToId, Map stageToSchsystemMap, Integer currentStageId) { - for (BaseTree node : treeList) { + for (BoaGroup node : treeList) { // 1. 检查当前节点是否为学段(名称匹配),更新最近学段ID Integer newStageId = currentStageId; if (stageNameToId.containsKey(node.getName())) { @@ -852,22 +847,22 @@ public class ComSchTreeService implements IComSchTreeService { private Map idToNameCache; - private void buildCache(BaseTree node) { + private void buildCache(BoaGroup node) { if (node == null) return; idToNameCache.put(node.getId(), node.getName()); if (node.getChildren() != null) { - for (BaseTree child : node.getChildren()) { + for (BoaGroup child : node.getChildren()) { buildCache(child); } } } - private List flattenTree(BaseTree root) { - List result = new ArrayList<>(); + private List flattenTree(BoaGroup root) { + List result = new ArrayList<>(); if (root == null) return result; result.add(root); if (root.getChildren() != null) { - for (BaseTree child : root.getChildren()) { + for (BoaGroup child : root.getChildren()) { result.addAll(flattenTree(child)); } } @@ -877,13 +872,13 @@ public class ComSchTreeService implements IComSchTreeService { /** * 递归扁平化树,收集指定类型的节点 */ - private void flattenTreeByType(BaseTree node, int type, List collector) { + private void flattenTreeByType(BoaGroup node, int type, List collector) { if (node == null) return; if (node.getType() != null && node.getType() == type) { collector.add(node); } if (node.getChildren() != null) { - for (BaseTree child : node.getChildren()) { + for (BoaGroup child : node.getChildren()) { flattenTreeByType(child, type, collector); } } diff --git a/src/main/java/com/yunhai/bschclassmanage/service/impl/SchTeacherService.java b/src/main/java/com/yunhai/bschclassmanage/service/impl/SchTeacherService.java index 53f9129..abc697d 100644 --- a/src/main/java/com/yunhai/bschclassmanage/service/impl/SchTeacherService.java +++ b/src/main/java/com/yunhai/bschclassmanage/service/impl/SchTeacherService.java @@ -10,9 +10,9 @@ import com.yunhai.bschclassmanage.model.constant.LogModule; import com.yunhai.bschclassmanage.model.dto.SchTeacherDTO; import com.yunhai.bschclassmanage.model.dto.TeaTransferGroupDTO; import com.yunhai.bschclassmanage.model.dto.TeaTransferInGroupDTO; -import com.yunhai.bschclassmanage.model.tool.BaseTree; import com.yunhai.bschclassmanage.model.vo.*; import com.yunhai.bschclassmanage.service.IComSchTreeService; +import com.yunhai.platform.utils.model.BoaGroup; import com.yunhai.bschclassmanage.service.ISchTeacherService; import com.yunhai.bschclassmanage.service.impl.teacher.*; import com.yunhai.bschclassmanage.utils.ModelMapperUtil; @@ -244,12 +244,12 @@ public class SchTeacherService implements ISchTeacherService { return Collections.emptyList(); } - BaseTree teacherGroupTree = comSchTreeService.getTeaTree(schId); + BoaGroup teacherGroupTree = comSchTreeService.getTeaTree(schId); if (teacherGroupTree == null) { return Collections.emptyList(); } - List allNodes = flattenTree(teacherGroupTree); + List allNodes = flattenTree(teacherGroupTree); return allNodes.stream() .filter(node -> groupIds.contains(node.getId())) @@ -311,14 +311,14 @@ public class SchTeacherService implements ISchTeacherService { /** * 递归扁平化树,返回所有节点列表(包括根节点) */ - private List flattenTree(BaseTree root) { - List result = new ArrayList<>(); + private List flattenTree(BoaGroup root) { + List result = new ArrayList<>(); if (root == null) { return result; } result.add(root); if (root.getChildren() != null) { - for (BaseTree child : root.getChildren()) { + for (BoaGroup child : root.getChildren()) { result.addAll(flattenTree(child)); } }