From ce0812211cc42a276a5f357aa5ec8ca30e30d700 Mon Sep 17 00:00:00 2001 From: wanghu Date: Fri, 14 Aug 2026 12:19:14 +0800 Subject: [PATCH] 1 --- pom.xml | 6 + .../Repository/SchClassHistoryRepository.java | 2 +- .../Repository/SchClassRepository.java | 29 ++- .../Repository/SchDicCourseRepository.java | 2 +- .../Repository/SchDicMajorsRepository.java | 2 +- .../Repository/SchStudentRepository.java | 2 +- .../Repository/SchTeacherGroupRepository.java | 2 +- .../Repository/SchTeacherRepository.java | 2 +- .../bschclassmanage/config/AsyncConfig.java | 14 + .../controller/api/UserController.java | 5 +- .../oapi/sync/SyncGroupController.java | 6 +- .../oapi/sync/SyncUserController.java | 11 +- .../bschclassmanage/model/tool/BaseUser.java | 48 ---- .../model/tool/SyncGroupDTO.java | 15 ++ .../service/ISyncGroupService.java | 8 +- .../service/ISyncUserService.java | 5 +- .../impl/classes/SchClassUpgradeService.java | 116 ++++----- .../service/impl/sync/SyncGroupService.java | 5 +- .../service/impl/sync/SyncUserService.java | 241 +++++++++--------- .../bschclassmanage/utils/OtherUtil.java | 18 +- 20 files changed, 260 insertions(+), 279 deletions(-) delete mode 100644 src/main/java/com/yunhai/bschclassmanage/model/tool/BaseUser.java create mode 100644 src/main/java/com/yunhai/bschclassmanage/model/tool/SyncGroupDTO.java diff --git a/pom.xml b/pom.xml index 7225de0..bd3f0eb 100644 --- a/pom.xml +++ b/pom.xml @@ -47,6 +47,12 @@ spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-jdbc + + com.alibaba.cloud diff --git a/src/main/java/com/yunhai/bschclassmanage/Repository/SchClassHistoryRepository.java b/src/main/java/com/yunhai/bschclassmanage/Repository/SchClassHistoryRepository.java index 3b6f91d..8713adc 100644 --- a/src/main/java/com/yunhai/bschclassmanage/Repository/SchClassHistoryRepository.java +++ b/src/main/java/com/yunhai/bschclassmanage/Repository/SchClassHistoryRepository.java @@ -2,7 +2,7 @@ package com.yunhai.bschclassmanage.Repository; import com.yunhai.bschclassmanage.entity.SchClass; import com.yunhai.bschclassmanage.entity.SchClassHistory; -import io.lettuce.core.dynamic.annotation.Param; +import org.springframework.data.repository.query.Param; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/com/yunhai/bschclassmanage/Repository/SchClassRepository.java b/src/main/java/com/yunhai/bschclassmanage/Repository/SchClassRepository.java index 3d5159d..78e7205 100644 --- a/src/main/java/com/yunhai/bschclassmanage/Repository/SchClassRepository.java +++ b/src/main/java/com/yunhai/bschclassmanage/Repository/SchClassRepository.java @@ -1,14 +1,16 @@ package com.yunhai.bschclassmanage.Repository; import com.yunhai.bschclassmanage.entity.SchClass; -import io.lettuce.core.dynamic.annotation.Param; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import java.util.Date; import java.util.List; import java.util.Optional; @@ -66,4 +68,29 @@ public interface SchClassRepository extends JpaRepository, Jp */ @Query("SELECT MIN(c.enrollYear) FROM SchClass c WHERE c.schId = :schId AND c.gradeId IN :gradeIds") Integer findMinEnrollYear(@Param("schId") Integer schId, @Param("gradeIds") List gradeIds); + + /** + * 学年升级:指定学校下、给定年级(非最高年级)的未毕业班级整体升一级 + * 使用 grade_id = grade_id + 1 的 SET 语义,单次语句原子完成,避免逐班加载与保存 + * @param schId 学校ID(安全过滤) + * @param gradeIds 需要升级的年级ID列表(非最高年级) + * @param now 修改时间 + * @return 实际受影响行数 + */ + @Modifying(clearAutomatically = true) + @Query("UPDATE SchClass c SET c.gradeId = c.gradeId + 1, c.updateDate = :now, c.updateUserId = 0 " + + "WHERE c.schId = :schId AND c.gradeId IN :gradeIds AND c.graduate = false") + int batchUpgradeGrade(@Param("schId") Integer schId, @Param("gradeIds") List gradeIds, @Param("now") Date now); + + /** + * 学年升级:指定学校下、最高年级的未毕业班级标记为毕业 + * @param schId 学校ID(安全过滤) + * @param maxGrade 最高年级ID + * @param now 修改时间 + * @return 实际受影响行数 + */ + @Modifying(clearAutomatically = true) + @Query("UPDATE SchClass c SET c.graduate = true, c.updateDate = :now, c.updateUserId = 0 " + + "WHERE c.schId = :schId AND c.gradeId = :maxGrade AND c.graduate = false") + int batchGraduate(@Param("schId") Integer schId, @Param("maxGrade") Integer maxGrade, @Param("now") Date now); } diff --git a/src/main/java/com/yunhai/bschclassmanage/Repository/SchDicCourseRepository.java b/src/main/java/com/yunhai/bschclassmanage/Repository/SchDicCourseRepository.java index c938e7d..0dd72f9 100644 --- a/src/main/java/com/yunhai/bschclassmanage/Repository/SchDicCourseRepository.java +++ b/src/main/java/com/yunhai/bschclassmanage/Repository/SchDicCourseRepository.java @@ -2,7 +2,7 @@ package com.yunhai.bschclassmanage.Repository; import com.yunhai.bschclassmanage.entity.SchDicCampus; import com.yunhai.bschclassmanage.entity.SchDicCourse; -import io.lettuce.core.dynamic.annotation.Param; +import org.springframework.data.repository.query.Param; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/com/yunhai/bschclassmanage/Repository/SchDicMajorsRepository.java b/src/main/java/com/yunhai/bschclassmanage/Repository/SchDicMajorsRepository.java index 2a7579a..5a9e955 100644 --- a/src/main/java/com/yunhai/bschclassmanage/Repository/SchDicMajorsRepository.java +++ b/src/main/java/com/yunhai/bschclassmanage/Repository/SchDicMajorsRepository.java @@ -1,7 +1,7 @@ package com.yunhai.bschclassmanage.Repository; import com.yunhai.bschclassmanage.entity.SchDicMajors; -import io.lettuce.core.dynamic.annotation.Param; +import org.springframework.data.repository.query.Param; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Query; diff --git a/src/main/java/com/yunhai/bschclassmanage/Repository/SchStudentRepository.java b/src/main/java/com/yunhai/bschclassmanage/Repository/SchStudentRepository.java index cb292af..19aee54 100644 --- a/src/main/java/com/yunhai/bschclassmanage/Repository/SchStudentRepository.java +++ b/src/main/java/com/yunhai/bschclassmanage/Repository/SchStudentRepository.java @@ -1,7 +1,7 @@ package com.yunhai.bschclassmanage.Repository; import com.yunhai.bschclassmanage.entity.SchStudent; -import io.lettuce.core.dynamic.annotation.Param; +import org.springframework.data.repository.query.Param; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Modifying; diff --git a/src/main/java/com/yunhai/bschclassmanage/Repository/SchTeacherGroupRepository.java b/src/main/java/com/yunhai/bschclassmanage/Repository/SchTeacherGroupRepository.java index 977604c..63b7cfb 100644 --- a/src/main/java/com/yunhai/bschclassmanage/Repository/SchTeacherGroupRepository.java +++ b/src/main/java/com/yunhai/bschclassmanage/Repository/SchTeacherGroupRepository.java @@ -6,7 +6,7 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import java.util.List; -import io.lettuce.core.dynamic.annotation.Param; +import org.springframework.data.repository.query.Param; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; diff --git a/src/main/java/com/yunhai/bschclassmanage/Repository/SchTeacherRepository.java b/src/main/java/com/yunhai/bschclassmanage/Repository/SchTeacherRepository.java index 81606cc..4632a14 100644 --- a/src/main/java/com/yunhai/bschclassmanage/Repository/SchTeacherRepository.java +++ b/src/main/java/com/yunhai/bschclassmanage/Repository/SchTeacherRepository.java @@ -2,7 +2,7 @@ package com.yunhai.bschclassmanage.Repository; import com.yunhai.bschclassmanage.entity.SchTeacher; import com.yunhai.bschclassmanage.model.vo.TeacherSchVO; -import io.lettuce.core.dynamic.annotation.Param; +import org.springframework.data.repository.query.Param; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/com/yunhai/bschclassmanage/config/AsyncConfig.java b/src/main/java/com/yunhai/bschclassmanage/config/AsyncConfig.java index 7670892..ae1e0db 100644 --- a/src/main/java/com/yunhai/bschclassmanage/config/AsyncConfig.java +++ b/src/main/java/com/yunhai/bschclassmanage/config/AsyncConfig.java @@ -6,6 +6,7 @@ import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.concurrent.Executor; +import java.util.concurrent.ThreadPoolExecutor; @Configuration @EnableAsync // 启用异步处理 @@ -21,4 +22,17 @@ public class AsyncConfig { executor.initialize(); // 初始化 return executor; } + + /** 新增:审计日志异步池(独立,避免与业务日志互相挤压;量大所以池更大) */ + @Bean(name = "auditLogExecutor") + public Executor auditLogExecutor() { + ThreadPoolTaskExecutor ex = new ThreadPoolTaskExecutor(); + ex.setCorePoolSize(4); + ex.setMaxPoolSize(16); + ex.setQueueCapacity(2000); + ex.setThreadNamePrefix("audit-log-"); + ex.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy()); // 队列满丢弃,不拖垮主流程 + ex.initialize(); + return ex; + } } \ No newline at end of file diff --git a/src/main/java/com/yunhai/bschclassmanage/controller/api/UserController.java b/src/main/java/com/yunhai/bschclassmanage/controller/api/UserController.java index 7daa050..09683b3 100644 --- a/src/main/java/com/yunhai/bschclassmanage/controller/api/UserController.java +++ b/src/main/java/com/yunhai/bschclassmanage/controller/api/UserController.java @@ -5,6 +5,7 @@ import com.yunhai.bschclassmanage.utils.UserContext; import com.yunhai.bschclassmanage.config.Config; import com.yunhai.platform.openapi.client.YschBaseOpenApi; import com.yunhai.platform.utils.model.BoaModule; +import com.yunhai.platform.utils.model.BoaUser; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -27,8 +28,8 @@ public class UserController { } @GetMapping("getUserInfo") - public UserContext.UserInfo getUserInfo() { - return UserContext.getCurrentUser(); + public BoaUser getUserInfo(BoaUser authUserWithDept) { + return authUserWithDept; } diff --git a/src/main/java/com/yunhai/bschclassmanage/controller/oapi/sync/SyncGroupController.java b/src/main/java/com/yunhai/bschclassmanage/controller/oapi/sync/SyncGroupController.java index fa38cc9..5304d95 100644 --- a/src/main/java/com/yunhai/bschclassmanage/controller/oapi/sync/SyncGroupController.java +++ b/src/main/java/com/yunhai/bschclassmanage/controller/oapi/sync/SyncGroupController.java @@ -2,6 +2,7 @@ package com.yunhai.bschclassmanage.controller.oapi.sync; import com.yunhai.bschclassmanage.config.Config; import com.yunhai.bschclassmanage.model.tool.BaseGroup; +import com.yunhai.bschclassmanage.model.tool.SyncGroupDTO; import com.yunhai.bschclassmanage.service.ISyncGroupService; import com.yunhai.bschclassmanage.utils.OtherUtil; import lombok.extern.slf4j.Slf4j; @@ -23,14 +24,14 @@ public class SyncGroupController { public void AddGroup(@RequestBody String jsonString) { log.info("jsonString="+jsonString); //判断 2,"学校组" 4, "班级组" - BaseGroup group = OtherUtil.jsonParseObject(jsonString, BaseGroup.class); + SyncGroupDTO group = OtherUtil.jsonParseObject(jsonString, SyncGroupDTO.class); syncGroupService.syncAddOrUpdate(group); } @PostMapping("UpdateGroup") public void UpdateGroup(@RequestBody String jsonString) { log.info("jsonString="+jsonString); - BaseGroup group = OtherUtil.jsonParseObject(jsonString, BaseGroup.class); + SyncGroupDTO group = OtherUtil.jsonParseObject(jsonString, SyncGroupDTO.class); syncGroupService.syncAddOrUpdate(group); } @@ -38,6 +39,7 @@ public class SyncGroupController { public void DeleteGroup(@RequestBody String jsonString) { log.info("jsonString="+jsonString); } + @PostMapping("SyncGroup") public void SyncGroup(@RequestBody String jsonString) { log.info("jsonString="+jsonString); 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 490f344..61fac2a 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 @@ -1,12 +1,9 @@ package com.yunhai.bschclassmanage.controller.oapi.sync; import com.yunhai.bschclassmanage.config.Config; -import com.yunhai.bschclassmanage.model.tool.BaseGroup; -import com.yunhai.bschclassmanage.model.tool.BaseUser; import com.yunhai.bschclassmanage.model.tool.DeleteUser; +import com.yunhai.bschclassmanage.model.tool.SyncUserDTO; import com.yunhai.bschclassmanage.service.ISyncUserService; -import com.yunhai.bschclassmanage.service.impl.sync.SyncGroupService; -import com.yunhai.bschclassmanage.service.impl.sync.SyncUserService; import com.yunhai.bschclassmanage.utils.OtherUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -25,14 +22,14 @@ public class SyncUserController { @PostMapping("AddUser") public void AddUser(@RequestBody String jsonString) { log.info("jsonString="+jsonString); - List userlist = OtherUtil.jsonParseList(jsonString, BaseUser.class); + List userlist = OtherUtil.jsonParseList(jsonString, SyncUserDTO.class); syncUserService.syncAddOrUpdate(userlist); } @PostMapping("UpdateUser") public void UpdateUser(@RequestBody String jsonString) { log.info("jsonString="+jsonString); - List userList = OtherUtil.jsonParseList(jsonString, BaseUser.class); + List userList = OtherUtil.jsonParseList(jsonString, SyncUserDTO.class); syncUserService.syncAddOrUpdate(userList); } @@ -46,7 +43,5 @@ public class SyncUserController { @PostMapping("SyncUser") public void SyncUser(@RequestBody String jsonString) { log.info("jsonString="+jsonString); - List deleteUserList= OtherUtil.jsonParseList(jsonString, DeleteUser.class); - syncUserService.deleteUser(deleteUserList); } } diff --git a/src/main/java/com/yunhai/bschclassmanage/model/tool/BaseUser.java b/src/main/java/com/yunhai/bschclassmanage/model/tool/BaseUser.java deleted file mode 100644 index c69e080..0000000 --- a/src/main/java/com/yunhai/bschclassmanage/model/tool/BaseUser.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.yunhai.bschclassmanage.model.tool; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.time.LocalDateTime; -import java.util.List; - -@AllArgsConstructor -@NoArgsConstructor -@Data -public class BaseUser { - private int id; - private Integer[] tags; - private String phone; - private String profilePicture; - private LocalDateTime birth; - private Integer[] depts; - private Integer status; - private String idcard; - private Integer type; - private String email; - private String name; - private String account; - private Integer gender; - private List groups; - private List roleList; - - @Data - @AllArgsConstructor - @NoArgsConstructor - public static class GroupInfo { - private Integer groupId; - private Integer no; - private Integer schoolId; - } - - @Data - @AllArgsConstructor - @NoArgsConstructor - public static class RoleInfo { - private Integer roleId; - private Integer schId; - } - - -} \ No newline at end of file diff --git a/src/main/java/com/yunhai/bschclassmanage/model/tool/SyncGroupDTO.java b/src/main/java/com/yunhai/bschclassmanage/model/tool/SyncGroupDTO.java new file mode 100644 index 0000000..d87d87e --- /dev/null +++ b/src/main/java/com/yunhai/bschclassmanage/model/tool/SyncGroupDTO.java @@ -0,0 +1,15 @@ +package com.yunhai.bschclassmanage.model.tool; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; + +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class SyncGroupDTO { + private Integer id; + private String code; + private String name; + private Integer type; + private Integer pid; + private Integer no; +} diff --git a/src/main/java/com/yunhai/bschclassmanage/service/ISyncGroupService.java b/src/main/java/com/yunhai/bschclassmanage/service/ISyncGroupService.java index 3d51024..57af8da 100644 --- a/src/main/java/com/yunhai/bschclassmanage/service/ISyncGroupService.java +++ b/src/main/java/com/yunhai/bschclassmanage/service/ISyncGroupService.java @@ -1,8 +1,6 @@ package com.yunhai.bschclassmanage.service; -import com.yunhai.bschclassmanage.entity.BaseDicGrade; -import com.yunhai.bschclassmanage.enums.NodeType; -import com.yunhai.bschclassmanage.model.tool.BaseGroup; +import com.yunhai.bschclassmanage.model.tool.SyncGroupDTO; import org.springframework.stereotype.Service; import java.util.List; @@ -10,7 +8,7 @@ import java.util.List; @Service public interface ISyncGroupService { - void syncAddOrUpdate(BaseGroup group); - void addOrUpdateSchool(BaseGroup group); + void syncAddOrUpdate(SyncGroupDTO group); + void addOrUpdateSchool(SyncGroupDTO group); } diff --git a/src/main/java/com/yunhai/bschclassmanage/service/ISyncUserService.java b/src/main/java/com/yunhai/bschclassmanage/service/ISyncUserService.java index bd8b6c1..f59dccf 100644 --- a/src/main/java/com/yunhai/bschclassmanage/service/ISyncUserService.java +++ b/src/main/java/com/yunhai/bschclassmanage/service/ISyncUserService.java @@ -1,8 +1,7 @@ package com.yunhai.bschclassmanage.service; -import com.yunhai.bschclassmanage.model.tool.BaseGroup; -import com.yunhai.bschclassmanage.model.tool.BaseUser; import com.yunhai.bschclassmanage.model.tool.DeleteUser; +import com.yunhai.bschclassmanage.model.tool.SyncUserDTO; import org.springframework.stereotype.Service; import java.util.List; @@ -10,7 +9,7 @@ import java.util.List; @Service public interface ISyncUserService { - void syncAddOrUpdate(List userList); + void syncAddOrUpdate(List userList); void deleteUser(List deleteUserList); } diff --git a/src/main/java/com/yunhai/bschclassmanage/service/impl/classes/SchClassUpgradeService.java b/src/main/java/com/yunhai/bschclassmanage/service/impl/classes/SchClassUpgradeService.java index 2e8d37c..a4765f1 100644 --- a/src/main/java/com/yunhai/bschclassmanage/service/impl/classes/SchClassUpgradeService.java +++ b/src/main/java/com/yunhai/bschclassmanage/service/impl/classes/SchClassUpgradeService.java @@ -5,119 +5,98 @@ import com.yunhai.bschclassmanage.entity.*; import com.yunhai.bschclassmanage.enums.SchType; import com.yunhai.bschclassmanage.model.tool.YearTerm; import com.yunhai.bschclassmanage.service.*; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import java.util.*; -import java.util.stream.Collectors; @Slf4j @Service +@RequiredArgsConstructor public class SchClassUpgradeService { - @Autowired - private SchClassCacheService schClassCacheService; - - @Autowired - private SchClassQueryService queryService; - - @Autowired - private SchClassRepository schClassRepository; - - @Autowired - private ISchBinSchsystemService schBinSchsystemService; - - @Autowired - private IBaseSchsystemGradeService baseSchsystemGradeService; - - @Autowired - private IBaseYearTermService baseYearTermService; - - @Autowired - private UpgradeLogRepository upgradeLogRepository; - - @Autowired - private JdbcTemplate jdbcTemplate; + private final SchClassCacheService schClassCacheService; + private final SchClassRepository schClassRepository; + private final ISchBinSchsystemService schBinSchsystemService; + private final IBaseSchsystemGradeService baseSchsystemGradeService; + private final IBaseYearTermService baseYearTermService; + private final UpgradeLogRepository upgradeLogRepository; + private final JdbcTemplate jdbcTemplate; public void upgradeAllClasses() { Date now = new Date(); // 获取当前学年学期 YearTerm yearTerm = baseYearTermService.getCurrentYearTerm(); + if (yearTerm == null) { + log.error("当前学年学期未配置,升级中止"); + return; + } int currentYear = yearTerm.getYear(); List schoolIds = schBinSchsystemService.getSchIds(); for (Integer schId : schoolIds) { - // ----- 2. 获取该校所有学制 ----- - List schsystemIds = schBinSchsystemService.getSchBinSchsystemIds(schId); + // 学校类型在整个学校维度内不变,提升到学制循环外只查一次 Integer schType = schBinSchsystemService.getSchType(schId); + + // ----- 获取该校所有学制 ----- + List schsystemIds = schBinSchsystemService.getSchBinSchsystemIds(schId); for (Integer sysId : schsystemIds) { - Boolean status=true; - if (upgradeLogRepository.existsBySchIdAndSysIdAndYearAndStatus(schId, sysId, currentYear,status)) { - log.info("学校{}学制{}学年{}学期已升级,跳过", schId, sysId, currentYear); - continue; // 直接返回,不做任何操作 + // 已成功升级过的(学年+学制)直接跳过,避免重复升级 + if (upgradeLogRepository.existsBySchIdAndSysIdAndYearAndStatus(schId, sysId, currentYear, true)) { + log.info("学校{}学制{}学年{}已成功升级,跳过", schId, sysId, currentYear); + continue; } + boolean success = true; try { - upgradeSingleSystem(schId,schType,sysId,now); + upgradeSingleSystem(schId, schType, sysId, now); log.info("学校{} 学制{} 学年{} 升级执行成功", schId, sysId, currentYear); } catch (Exception e) { + success = false; log.error("学校{}学制{}升级失败,已跳过,不影响其他学制", schId, sysId, e); - status=false; } - upgradeLogRepository.save(new UpgradeLog(schId, sysId, currentYear,now,status)); + // 无论成功失败都落升级日志,失败记录便于下次重试(existsBy...Status=true 不命中即可重跑) + try { + upgradeLogRepository.save(new UpgradeLog(schId, sysId, currentYear, now, success)); + } catch (Exception e) { + log.error("学校{}学制{}升级结果日志写入失败", schId, sysId, e); + } } - // 清除该校缓存 + // 升级完成后清除该校班级缓存(含毕业缓存),避免脏数据 schClassCacheService.clearClassCache(schId); + schClassCacheService.clearGradeateClassCache(schId); } } //升级业务逻辑 @Transactional(propagation = Propagation.REQUIRES_NEW) - protected void upgradeSingleSystem(Integer schId,Integer schType, Integer sysId, Date now) { - // 1. 检查是否已升级(利用数据库唯一索引防并发) - // 2. 执行升级逻辑 - // 3. 插入升级日志 - + protected void upgradeSingleSystem(Integer schId, Integer schType, Integer sysId, Date now) { + // 1. 获取该学制下的年级定义(轻量、带缓存),得到年级集合与最高年级 Map gradeMap = baseSchsystemGradeService.getGradeNameMap(schId, sysId, null); + if (gradeMap == null || gradeMap.isEmpty()) { + return; + } List gradeIds = new ArrayList<>(gradeMap.keySet()); Collections.sort(gradeIds); - if (gradeIds.isEmpty()) return; - Integer maxGrade = gradeIds.get(gradeIds.size() - 1); - // ----- 3. 升级非最高年级 ----- - for (Integer gradeId : gradeIds) { - if (gradeId.equals(maxGrade)) continue; - List classList = queryService.getSchClassCacheFilter(schId, gradeId, null); - if (classList.isEmpty()) continue; - - // 执行升级 - for (SchClass clazz : classList) { - if(schType.equals(SchType.BASICEDUCATION.getKey())) { - clazz.setGradeId(gradeId + 1);//基础教育年级+1 - } - clazz.setUpdateDate(now); - clazz.setUpdateUserId(0); + // 2. 非最高年级:基础教育整体升一级(SET 语义,单语句原子完成) + if (SchType.BASICEDUCATION.getKey().equals(schType)) { + List nonMaxGrades = new ArrayList<>(gradeIds.subList(0, gradeIds.size() - 1)); + if (!nonMaxGrades.isEmpty()) { + int updated = schClassRepository.batchUpgradeGrade(schId, nonMaxGrades, now); + log.info("学校{}学制{} 非最高年级升一级,影响班级数={}", schId, sysId, updated); } - schClassRepository.saveAll(classList); - } - - // ----- 4. 处理最高年级(毕业) ----- - List graduateClassList = queryService.getSchClassCacheFilter(schId, maxGrade, null); - if (!graduateClassList.isEmpty()) { - for (SchClass clazz : graduateClassList) { - clazz.setGraduate(true); - clazz.setUpdateDate(now); - clazz.setUpdateUserId(0); - } - schClassRepository.saveAll(graduateClassList); } + // 3. 最高年级:毕业 + int graduated = schClassRepository.batchGraduate(schId, maxGrade, now); + log.info("学校{}学制{} 最高年级(年级ID={})毕业,影响班级数={}", schId, sysId, maxGrade, graduated); } @@ -151,5 +130,4 @@ public class SchClassUpgradeService { } - -} \ No newline at end of file +} diff --git a/src/main/java/com/yunhai/bschclassmanage/service/impl/sync/SyncGroupService.java b/src/main/java/com/yunhai/bschclassmanage/service/impl/sync/SyncGroupService.java index 1634ecf..3b56873 100644 --- a/src/main/java/com/yunhai/bschclassmanage/service/impl/sync/SyncGroupService.java +++ b/src/main/java/com/yunhai/bschclassmanage/service/impl/sync/SyncGroupService.java @@ -4,6 +4,7 @@ import com.yunhai.bschclassmanage.Repository.SchInfoRepository; import com.yunhai.bschclassmanage.entity.SchInfo; import com.yunhai.bschclassmanage.enums.NodeType; import com.yunhai.bschclassmanage.model.tool.BaseGroup; +import com.yunhai.bschclassmanage.model.tool.SyncGroupDTO; import com.yunhai.bschclassmanage.service.ISyncGroupService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -19,7 +20,7 @@ public class SyncGroupService implements ISyncGroupService { private SchInfoRepository schInfoRepository; - public void syncAddOrUpdate(BaseGroup group) { + public void syncAddOrUpdate(SyncGroupDTO group) { //学校组 年级组 班级组 if(group.getType().equals(NodeType.SCHOOL.getId())){ @@ -37,7 +38,7 @@ public class SyncGroupService implements ISyncGroupService { } - public void addOrUpdateSchool(BaseGroup group) + public void addOrUpdateSchool(SyncGroupDTO group) { Optional schOpt=schInfoRepository.findBySchId(group.getId()); if(schOpt.isPresent()){ diff --git a/src/main/java/com/yunhai/bschclassmanage/service/impl/sync/SyncUserService.java b/src/main/java/com/yunhai/bschclassmanage/service/impl/sync/SyncUserService.java index b7c6084..26bc4f7 100644 --- a/src/main/java/com/yunhai/bschclassmanage/service/impl/sync/SyncUserService.java +++ b/src/main/java/com/yunhai/bschclassmanage/service/impl/sync/SyncUserService.java @@ -1,33 +1,31 @@ package com.yunhai.bschclassmanage.service.impl.sync; -import com.yunhai.bschclassmanage.Repository.SchInfoRepository; import com.yunhai.bschclassmanage.Repository.SchStudentRepository; import com.yunhai.bschclassmanage.Repository.SchTeacherGroupRepository; import com.yunhai.bschclassmanage.Repository.SchTeacherRepository; -import com.yunhai.bschclassmanage.entity.SchInfo; import com.yunhai.bschclassmanage.entity.SchStudent; import com.yunhai.bschclassmanage.entity.SchTeacher; import com.yunhai.bschclassmanage.entity.SchTeacherGroup; -import com.yunhai.bschclassmanage.enums.NodeType; import com.yunhai.bschclassmanage.enums.StuAbnormalStatus; import com.yunhai.bschclassmanage.enums.TransferStatus; import com.yunhai.bschclassmanage.enums.UserRole; -import com.yunhai.bschclassmanage.model.tool.BaseGroup; -import com.yunhai.bschclassmanage.model.tool.BaseUser; import com.yunhai.bschclassmanage.model.tool.DeleteUser; -import com.yunhai.bschclassmanage.service.ISchTeacherService; +import com.yunhai.bschclassmanage.model.tool.SyncUserDTO; +import com.yunhai.bschclassmanage.model.tool.SyncUserGroupDTO; +import com.yunhai.bschclassmanage.model.tool.SyncUserRoleDTO; import com.yunhai.bschclassmanage.service.ISyncUserService; import com.yunhai.bschclassmanage.service.impl.student.SchStudentCacheService; import com.yunhai.bschclassmanage.service.impl.teacher.SchTeacherCacheService; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.time.LocalDateTime; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; +@Slf4j @Service public class SyncUserService implements ISyncUserService { @@ -47,18 +45,25 @@ public class SyncUserService implements ISyncUserService { private SchTeacherCacheService schTeacherCacheService; // ==================== 同步入口 ==================== + @Override @Transactional(rollbackFor = Exception.class) - public void syncAddOrUpdate(List userList) { + public void syncAddOrUpdate(List userList) { if (userList == null || userList.isEmpty()) return; - // 按角色分组 - List syncStudentList = new ArrayList<>(); - List syncTeacherList = new ArrayList<>(); + // 按角色分组(每个用户只遍历一次 roleList) + List syncStudentList = new ArrayList<>(); + List syncTeacherList = new ArrayList<>(); - for (BaseUser user : userList) { - if (isStudent(user)) syncStudentList.add(user); - if (isTeacher(user)) syncTeacherList.add(user); + for (SyncUserDTO user : userList) { + Set roleIds = collectRoleIds(user); + if (isStudent(roleIds)) { + syncStudentList.add(user); + } else if (isTeacher(roleIds)) { + syncTeacherList.add(user); + } else { + log.error("用户:{} 角色不符合同步要求!", user.getAccount()); + } } Date now = new Date(); @@ -71,124 +76,112 @@ public class SyncUserService implements ISyncUserService { } } - @Override @Transactional(rollbackFor = Exception.class) public void deleteUser(List deleteUserList) { if (deleteUserList == null || deleteUserList.isEmpty()) return; - // 提取所有 userId - List allUserIds = deleteUserList.stream() - .map(DeleteUser::getUserId) - .filter(Objects::nonNull) - .distinct() - .collect(Collectors.toList()); - if (allUserIds.isEmpty()) return; - - // ---------- 1. 物理删除学生 ---------- - List studentDeletes = deleteUserList.stream() + // 过滤出有效的 (userId, groupId) 条目,仅计算一次 + List validDeletes = deleteUserList.stream() .filter(du -> du.getUserId() != null && du.getGroupId() != null) .collect(Collectors.toList()); - if (!studentDeletes.isEmpty()) { - // 批量查询学生 - List allStudents = schStudentRepository.findByStuIdIn(allUserIds); - Map studentMap = allStudents.stream() - .collect(Collectors.toMap( - s -> buildStudentKey(s.getStuId(), s.getGroupId()), - Function.identity() - )); - List toDelete = new ArrayList<>(); - for (DeleteUser du : studentDeletes) { - String key = buildStudentKey(du.getUserId(), du.getGroupId()); - SchStudent stu = studentMap.get(key); - if (stu != null) { - toDelete.add(stu); + if (validDeletes.isEmpty()) return; + + List allUserIds = validDeletes.stream() + .map(DeleteUser::getUserId) + .distinct() + .collect(Collectors.toList()); + + // ---------- 1. 物理删除学生 ---------- + List toDeleteStudents = new ArrayList<>(); + Map studentMap = schStudentRepository.findByStuIdIn(allUserIds).stream() + .collect(Collectors.toMap( + s -> buildStudentKey(s.getStuId(), s.getGroupId()), + Function.identity() + )); + for (DeleteUser du : validDeletes) { + SchStudent stu = studentMap.get(buildStudentKey(du.getUserId(), du.getGroupId())); + if (stu != null) { + toDeleteStudents.add(stu); + } + } + if (!toDeleteStudents.isEmpty()) { + // 按 (schId, classId) 去重后清除缓存 + Map> classKeys = new HashMap<>(); + for (SchStudent stu : toDeleteStudents) { + if (stu.getSchId() != null && stu.getGroupId() != null) { + classKeys.computeIfAbsent(stu.getSchId(), k -> new HashSet<>()).add(stu.getGroupId()); } } - if (!toDelete.isEmpty()) { - // 先清除缓存(需要 schId,可从学生实体获取或查询) - for (SchStudent stu : toDelete) { - // 假设 SchStudent 有 schId 字段,若没有则需通过 groupId 查询 - Integer schId = stu.getSchId(); // 若没有,调用 service 查询 - if (schId != null) { - schStudentCacheService.clearClassStudentCache(schId, stu.getGroupId()); - } - } - // 物理删除 - schStudentRepository.deleteAll(toDelete); - } + classKeys.forEach((schId, classIds) -> schStudentCacheService.clearClassStudentCaches(schId, classIds)); + schStudentRepository.deleteAll(toDeleteStudents); } // ---------- 2. 物理删除教师分组(不删教师主体) ---------- - List teacherDeletes = deleteUserList.stream() - .filter(du -> du.getUserId() != null && du.getGroupId() != null) - .collect(Collectors.toList()); - if (!teacherDeletes.isEmpty()) { - // 查询所有涉及的分组 - List allGroups = schTeacherGroupRepository.findByTeaIdIn(allUserIds); - Map groupMap = allGroups.stream() - .collect(Collectors.toMap( - g -> buildTeacherGroupKey(g.getTeaId(), g.getGroupId()), - Function.identity() - )); - List toDeleteGroups = new ArrayList<>(); - for (DeleteUser du : teacherDeletes) { - String key = buildTeacherGroupKey(du.getUserId(), du.getGroupId()); - SchTeacherGroup group = groupMap.get(key); - if (group != null) { - toDeleteGroups.add(group); - } - } - - if (!toDeleteGroups.isEmpty()) { - // 按学校维度分组,用于缓存清除和剩余分组检查 - Map> groupsBySchool = toDeleteGroups.stream() - .collect(Collectors.groupingBy(SchTeacherGroup::getSchId)); - - // 先清除缓存(针对每个分组所属学校) - for (SchTeacherGroup group : toDeleteGroups) { - schTeacherCacheService.clearTeacherCache(group.getSchId()); - } - - // 物理删除分组 - schTeacherGroupRepository.deleteAll(toDeleteGroups); - + // 同一 (teaId, groupId) 可能跨多所学校,用 List 收集避免重复键被丢弃 + Map> groupMap = schTeacherGroupRepository.findByTeaIdIn(allUserIds).stream() + .collect(Collectors.groupingBy(g -> buildTeacherGroupKey(g.getTeaId(), g.getGroupId()))); + List toDeleteGroups = new ArrayList<>(); + for (DeleteUser du : validDeletes) { + List groups = groupMap.get(buildTeacherGroupKey(du.getUserId(), du.getGroupId())); + if (groups != null) { + toDeleteGroups.addAll(groups); } } + if (!toDeleteGroups.isEmpty()) { + // 按学校维度去重后清除缓存 + Set schIds = toDeleteGroups.stream() + .map(SchTeacherGroup::getSchId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + schTeacherCacheService.clearTeacherCaches(schIds); + schTeacherGroupRepository.deleteAll(toDeleteGroups); + } } // ==================== 角色判断 ==================== - private boolean isStudent(BaseUser user) { - if (user.getRoleList() == null) return false; + + /** + * 提取用户的角色 id 集合(去重、过滤空值),每个用户仅遍历一次。 + */ + private Set collectRoleIds(SyncUserDTO user) { + if (user.getRoleList() == null) return Collections.emptySet(); return user.getRoleList().stream() - .anyMatch(r -> r.getRoleId() != null && r.getRoleId().equals(UserRole.STU_USER.getValue())); + .filter(Objects::nonNull) + .map(SyncUserRoleDTO::getRoleId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); } - private boolean isTeacher(BaseUser user) { - if (user.getRoleList() == null) return false; - return user.getRoleList().stream() - .anyMatch(r -> r.getRoleId() != null && - (r.getRoleId().equals(UserRole.TEACHER_USER.getValue()) || - r.getRoleId().equals(UserRole.SCH_LEADER_USER.getValue()))); + private boolean isStudent(Set roleIds) { + return roleIds.contains(UserRole.STU_USER.getValue()); + } + + private boolean isTeacher(Set roleIds) { + return roleIds.contains(UserRole.TEACHER_USER.getValue()) + || roleIds.contains(UserRole.SCH_LEADER_USER.getValue()); } // ==================== 学生同步 ==================== - private void syncStudents(List studentUsers, Date now) { - List stuIds = studentUsers.stream().map(BaseUser::getId).distinct().collect(Collectors.toList()); + + private void syncStudents(List studentUsers, Date now) { + List stuIds = studentUsers.stream().map(SyncUserDTO::getId).distinct().collect(Collectors.toList()); // 已有学生记录 (stuId, groupId) -> SchStudent - Map studentMap = schStudentRepository.findByStuIdIn(stuIds) - .stream() + Map studentMap = schStudentRepository.findByStuIdIn(stuIds).stream() .collect(Collectors.toMap( s -> buildStudentKey(s.getStuId(), s.getGroupId()), Function.identity() )); List saveList = new ArrayList<>(); + // 受影响班级缓存键 (schId -> classId 集合),用于同步后失效 + Map> classKeys = new HashMap<>(); - for (BaseUser user : studentUsers) { + for (SyncUserDTO user : studentUsers) { if (user.getGroups() == null) continue; - for (BaseUser.GroupInfo group : user.getGroups()) { + + for (SyncUserGroupDTO group : user.getGroups()) { Integer groupId = group.getGroupId(); Integer schId = group.getSchoolId(); if (groupId == null || schId == null) continue; @@ -201,21 +194,18 @@ public class SyncUserService implements ISyncUserService { student = createStudent(user, group, now); } saveList.add(student); + classKeys.computeIfAbsent(schId, k -> new HashSet<>()).add(groupId); } } if (!saveList.isEmpty()) { schStudentRepository.saveAll(saveList); - // 缓存:将学生加入班级缓存(Set结构) -// for (SchStudent stu : saveList) { -// if (stu.getSchId() != null && stu.getGroupId() != null) { -// schStudentCacheService.addClassStudentCache(stu.getSchId(), stu.getGroupId(), stu.getStuId()); -// } -// } + // 同步后使受影响班级的学生缓存失效,避免读到旧数据 + classKeys.forEach((schId, classIds) -> schStudentCacheService.clearClassStudentCaches(schId, classIds)); } } - private void updateStudent(SchStudent student, BaseUser user, BaseUser.GroupInfo group, Date now) { + private void updateStudent(SchStudent student, SyncUserDTO user, SyncUserGroupDTO group, Date now) { student.setAccount(user.getAccount()); student.setName(user.getName()); student.setGender(user.getGender()); @@ -228,7 +218,7 @@ public class SyncUserService implements ISyncUserService { student.setUpdateDate(now); } - private SchStudent createStudent(BaseUser user, BaseUser.GroupInfo group, Date now) { + private SchStudent createStudent(SyncUserDTO user, SyncUserGroupDTO group, Date now) { SchStudent student = new SchStudent(); student.setStuId(user.getId()); student.setAccount(user.getAccount()); @@ -248,17 +238,16 @@ public class SyncUserService implements ISyncUserService { } // ==================== 教师同步 ==================== - private void syncTeachers(List teacherUsers, Date now) { - List teaIds = teacherUsers.stream().map(BaseUser::getId).distinct().collect(Collectors.toList()); + + private void syncTeachers(List teacherUsers, Date now) { + List teaIds = teacherUsers.stream().map(SyncUserDTO::getId).distinct().collect(Collectors.toList()); // 教师主体(唯一) - Map teacherMap = schTeacherRepository.findByTeaIdIn(teaIds) - .stream() + Map teacherMap = schTeacherRepository.findByTeaIdIn(teaIds).stream() .collect(Collectors.toMap(SchTeacher::getTeaId, Function.identity())); // 教师分组 (teaId, groupId, schId) -> SchTeacherGroup - Map groupMap = schTeacherGroupRepository.findByTeaIdIn(teaIds) - .stream() + Map groupMap = schTeacherGroupRepository.findByTeaIdIn(teaIds).stream() .collect(Collectors.toMap( g -> buildTeacherGroupKeyWithSchId(g.getTeaId(), g.getGroupId(), g.getSchId()), Function.identity() @@ -266,8 +255,10 @@ public class SyncUserService implements ISyncUserService { List saveTeacherList = new ArrayList<>(); List saveGroupList = new ArrayList<>(); + // 受影响学校缓存键,用于同步后失效 + Set schIds = new HashSet<>(); - for (BaseUser user : teacherUsers) { + for (SyncUserDTO user : teacherUsers) { // 教师主体(只存一条) SchTeacher teacher = teacherMap.get(user.getId()); if (teacher != null) { @@ -279,7 +270,7 @@ public class SyncUserService implements ISyncUserService { // 教师分组(遍历 groups) if (user.getGroups() == null) continue; - for (BaseUser.GroupInfo group : user.getGroups()) { + for (SyncUserGroupDTO group : user.getGroups()) { Integer groupId = group.getGroupId(); Integer schId = group.getSchoolId(); if (groupId == null || schId == null) continue; @@ -293,6 +284,7 @@ public class SyncUserService implements ISyncUserService { tg = createTeacherGroup(user, group, now); } saveGroupList.add(tg); + schIds.add(schId); } } @@ -301,18 +293,14 @@ public class SyncUserService implements ISyncUserService { } if (!saveGroupList.isEmpty()) { schTeacherGroupRepository.saveAll(saveGroupList); -// // 缓存:更新教师缓存(学校维度) -// // 为简单起见,清除每个涉及的学校缓存,然后重新添加该学校所有教师(但这里我们只添加本次更新的教师,因为Set可重复) -// // 更稳健:清除后重建,但为了减少数据库查询,我们使用 add 方法 -// for (SchTeacherGroup g : saveGroupList) { -// if (g.getSchId() != null) { -// schTeacherCacheService.addSchoolTeacherCache(g.getSchId(), g.getTeaId()); -// } -// } + } + // 同步后使受影响学校的教师缓存失效 + if (!schIds.isEmpty()) { + schTeacherCacheService.clearTeacherCaches(schIds); } } - private void updateTeacher(SchTeacher teacher, BaseUser user, Date now) { + private void updateTeacher(SchTeacher teacher, SyncUserDTO user, Date now) { teacher.setAccount(user.getAccount()); teacher.setName(user.getName()); teacher.setGender(user.getGender()); @@ -322,7 +310,7 @@ public class SyncUserService implements ISyncUserService { teacher.setUpdateDate(now); } - private SchTeacher createTeacher(BaseUser user, Date now) { + private SchTeacher createTeacher(SyncUserDTO user, Date now) { SchTeacher teacher = new SchTeacher(); teacher.setTeaId(user.getId()); teacher.setAccount(user.getAccount()); @@ -336,7 +324,7 @@ public class SyncUserService implements ISyncUserService { return teacher; } - private SchTeacherGroup createTeacherGroup(BaseUser user, BaseUser.GroupInfo group, Date now) { + private SchTeacherGroup createTeacherGroup(SyncUserDTO user, SyncUserGroupDTO group, Date now) { SchTeacherGroup tg = new SchTeacherGroup(); tg.setTeaId(user.getId()); tg.setGroupId(group.getGroupId()); @@ -346,6 +334,7 @@ public class SyncUserService implements ISyncUserService { } // ==================== 辅助键生成 ==================== + private String buildStudentKey(Integer stuId, Integer groupId) { return stuId + "_" + groupId; } diff --git a/src/main/java/com/yunhai/bschclassmanage/utils/OtherUtil.java b/src/main/java/com/yunhai/bschclassmanage/utils/OtherUtil.java index 5b2c4b3..efcb833 100644 --- a/src/main/java/com/yunhai/bschclassmanage/utils/OtherUtil.java +++ b/src/main/java/com/yunhai/bschclassmanage/utils/OtherUtil.java @@ -263,12 +263,16 @@ public class OtherUtil { return null; } try { - // 使用 TypeReference 保留泛型信息,避免类型擦除 - return objectMapper.readValue(json, new TypeReference>() {}); + // 注意:new TypeReference>() {} 会因泛型擦除退化为原始 List, + // 元素被反序列化为 LinkedHashMap,后续强转目标类型会抛 ClassCastException。 + // 必须用 TypeFactory 动态构建 List 的 JavaType 才能保留元素类型。 + JavaType listType = objectMapper.getTypeFactory() + .constructCollectionType(List.class, targetClass); + return objectMapper.readValue(json, listType); } catch (Exception e) { System.err.println("解析 JSON 数组失败,json 前缀: " + json); e.printStackTrace(); - return null; + throw new DException("解析 JSON 数组失败!"); } } @@ -286,9 +290,9 @@ public class OtherUtil { try { return objectMapper.readValue(json, targetClass); } catch (Exception e) { - System.err.println("解析 JSON 数组失败,json 前缀: " + json); + System.err.println("解析 JSON 对象失败,json 前缀: " + json); e.printStackTrace(); - return null; + throw new DException("解析 JSON 对象失败!"); } } @@ -305,7 +309,7 @@ public class OtherUtil { } catch (Exception e) { System.err.println("解析 JSON 数组失败: " + json); e.printStackTrace(); - return null; + throw new DException("解析 JSON 数组失败!"); } } @@ -320,7 +324,7 @@ public class OtherUtil { } catch (Exception e) { System.err.println("解析 JSON 数组失败: " + json); e.printStackTrace(); - return null; + throw new DException("解析 JSON 数组失败!"); } }