This commit is contained in:
2026-08-14 12:19:17 +08:00
parent 7a549c87c6
commit ce0812211c
20 changed files with 260 additions and 279 deletions
+6
View File
@@ -47,6 +47,12 @@
<artifactId>spring-boot-starter-data-jpa</artifactId> <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency> </dependency>
<!-- 显式引入 JDBC(提供 JdbcTemplate,用于 backupAllSnapshot 的批量快照插入) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- Nacos 配置中心(替代 Spring Cloud Config)。Spring Cloud Alibaba 2021.0.5 适配 Boot 2.7 --> <!-- Nacos 配置中心(替代 Spring Cloud Config)。Spring Cloud Alibaba 2021.0.5 适配 Boot 2.7 -->
<dependency> <dependency>
<groupId>com.alibaba.cloud</groupId> <groupId>com.alibaba.cloud</groupId>
@@ -2,7 +2,7 @@ package com.yunhai.bschclassmanage.Repository;
import com.yunhai.bschclassmanage.entity.SchClass; import com.yunhai.bschclassmanage.entity.SchClass;
import com.yunhai.bschclassmanage.entity.SchClassHistory; 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.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
@@ -1,14 +1,16 @@
package com.yunhai.bschclassmanage.Repository; package com.yunhai.bschclassmanage.Repository;
import com.yunhai.bschclassmanage.entity.SchClass; import com.yunhai.bschclassmanage.entity.SchClass;
import io.lettuce.core.dynamic.annotation.Param;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor; 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.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@@ -66,4 +68,29 @@ public interface SchClassRepository extends JpaRepository<SchClass, Integer>, Jp
*/ */
@Query("SELECT MIN(c.enrollYear) FROM SchClass c WHERE c.schId = :schId AND c.gradeId IN :gradeIds") @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<Integer> gradeIds); Integer findMinEnrollYear(@Param("schId") Integer schId, @Param("gradeIds") List<Integer> 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<Integer> 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);
} }
@@ -2,7 +2,7 @@ package com.yunhai.bschclassmanage.Repository;
import com.yunhai.bschclassmanage.entity.SchDicCampus; import com.yunhai.bschclassmanage.entity.SchDicCampus;
import com.yunhai.bschclassmanage.entity.SchDicCourse; 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.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
@@ -1,7 +1,7 @@
package com.yunhai.bschclassmanage.Repository; package com.yunhai.bschclassmanage.Repository;
import com.yunhai.bschclassmanage.entity.SchDicMajors; 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.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
@@ -1,7 +1,7 @@
package com.yunhai.bschclassmanage.Repository; package com.yunhai.bschclassmanage.Repository;
import com.yunhai.bschclassmanage.entity.SchStudent; 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.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
@@ -6,7 +6,7 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import java.util.List; 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.Modifying;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
@@ -2,7 +2,7 @@ package com.yunhai.bschclassmanage.Repository;
import com.yunhai.bschclassmanage.entity.SchTeacher; import com.yunhai.bschclassmanage.entity.SchTeacher;
import com.yunhai.bschclassmanage.model.vo.TeacherSchVO; 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.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
@@ -6,6 +6,7 @@ import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration @Configuration
@EnableAsync // 启用异步处理 @EnableAsync // 启用异步处理
@@ -21,4 +22,17 @@ public class AsyncConfig {
executor.initialize(); // 初始化 executor.initialize(); // 初始化
return executor; 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;
}
} }
@@ -5,6 +5,7 @@ import com.yunhai.bschclassmanage.utils.UserContext;
import com.yunhai.bschclassmanage.config.Config; import com.yunhai.bschclassmanage.config.Config;
import com.yunhai.platform.openapi.client.YschBaseOpenApi; import com.yunhai.platform.openapi.client.YschBaseOpenApi;
import com.yunhai.platform.utils.model.BoaModule; 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.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
@@ -27,8 +28,8 @@ public class UserController {
} }
@GetMapping("getUserInfo") @GetMapping("getUserInfo")
public UserContext.UserInfo getUserInfo() { public BoaUser getUserInfo(BoaUser authUserWithDept) {
return UserContext.getCurrentUser(); return authUserWithDept;
} }
@@ -2,6 +2,7 @@ package com.yunhai.bschclassmanage.controller.oapi.sync;
import com.yunhai.bschclassmanage.config.Config; import com.yunhai.bschclassmanage.config.Config;
import com.yunhai.bschclassmanage.model.tool.BaseGroup; import com.yunhai.bschclassmanage.model.tool.BaseGroup;
import com.yunhai.bschclassmanage.model.tool.SyncGroupDTO;
import com.yunhai.bschclassmanage.service.ISyncGroupService; import com.yunhai.bschclassmanage.service.ISyncGroupService;
import com.yunhai.bschclassmanage.utils.OtherUtil; import com.yunhai.bschclassmanage.utils.OtherUtil;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -23,14 +24,14 @@ public class SyncGroupController {
public void AddGroup(@RequestBody String jsonString) { public void AddGroup(@RequestBody String jsonString) {
log.info("jsonString="+jsonString); log.info("jsonString="+jsonString);
//判断 2,"学校组" 4, "班级组" //判断 2,"学校组" 4, "班级组"
BaseGroup group = OtherUtil.jsonParseObject(jsonString, BaseGroup.class); SyncGroupDTO group = OtherUtil.jsonParseObject(jsonString, SyncGroupDTO.class);
syncGroupService.syncAddOrUpdate(group); syncGroupService.syncAddOrUpdate(group);
} }
@PostMapping("UpdateGroup") @PostMapping("UpdateGroup")
public void UpdateGroup(@RequestBody String jsonString) { public void UpdateGroup(@RequestBody String jsonString) {
log.info("jsonString="+jsonString); log.info("jsonString="+jsonString);
BaseGroup group = OtherUtil.jsonParseObject(jsonString, BaseGroup.class); SyncGroupDTO group = OtherUtil.jsonParseObject(jsonString, SyncGroupDTO.class);
syncGroupService.syncAddOrUpdate(group); syncGroupService.syncAddOrUpdate(group);
} }
@@ -38,6 +39,7 @@ public class SyncGroupController {
public void DeleteGroup(@RequestBody String jsonString) { public void DeleteGroup(@RequestBody String jsonString) {
log.info("jsonString="+jsonString); log.info("jsonString="+jsonString);
} }
@PostMapping("SyncGroup") @PostMapping("SyncGroup")
public void SyncGroup(@RequestBody String jsonString) { public void SyncGroup(@RequestBody String jsonString) {
log.info("jsonString="+jsonString); log.info("jsonString="+jsonString);
@@ -1,12 +1,9 @@
package com.yunhai.bschclassmanage.controller.oapi.sync; package com.yunhai.bschclassmanage.controller.oapi.sync;
import com.yunhai.bschclassmanage.config.Config; 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.DeleteUser;
import com.yunhai.bschclassmanage.model.tool.SyncUserDTO;
import com.yunhai.bschclassmanage.service.ISyncUserService; 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 com.yunhai.bschclassmanage.utils.OtherUtil;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -25,14 +22,14 @@ public class SyncUserController {
@PostMapping("AddUser") @PostMapping("AddUser")
public void AddUser(@RequestBody String jsonString) { public void AddUser(@RequestBody String jsonString) {
log.info("jsonString="+jsonString); log.info("jsonString="+jsonString);
List<BaseUser> userlist = OtherUtil.jsonParseList(jsonString, BaseUser.class); List<SyncUserDTO> userlist = OtherUtil.jsonParseList(jsonString, SyncUserDTO.class);
syncUserService.syncAddOrUpdate(userlist); syncUserService.syncAddOrUpdate(userlist);
} }
@PostMapping("UpdateUser") @PostMapping("UpdateUser")
public void UpdateUser(@RequestBody String jsonString) { public void UpdateUser(@RequestBody String jsonString) {
log.info("jsonString="+jsonString); log.info("jsonString="+jsonString);
List<BaseUser> userList = OtherUtil.jsonParseList(jsonString, BaseUser.class); List<SyncUserDTO> userList = OtherUtil.jsonParseList(jsonString, SyncUserDTO.class);
syncUserService.syncAddOrUpdate(userList); syncUserService.syncAddOrUpdate(userList);
} }
@@ -46,7 +43,5 @@ public class SyncUserController {
@PostMapping("SyncUser") @PostMapping("SyncUser")
public void SyncUser(@RequestBody String jsonString) { public void SyncUser(@RequestBody String jsonString) {
log.info("jsonString="+jsonString); log.info("jsonString="+jsonString);
List<DeleteUser> deleteUserList= OtherUtil.jsonParseList(jsonString, DeleteUser.class);
syncUserService.deleteUser(deleteUserList);
} }
} }
@@ -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<GroupInfo> groups;
private List<RoleInfo> 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;
}
}
@@ -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;
}
@@ -1,8 +1,6 @@
package com.yunhai.bschclassmanage.service; package com.yunhai.bschclassmanage.service;
import com.yunhai.bschclassmanage.entity.BaseDicGrade; import com.yunhai.bschclassmanage.model.tool.SyncGroupDTO;
import com.yunhai.bschclassmanage.enums.NodeType;
import com.yunhai.bschclassmanage.model.tool.BaseGroup;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.List; import java.util.List;
@@ -10,7 +8,7 @@ import java.util.List;
@Service @Service
public interface ISyncGroupService { public interface ISyncGroupService {
void syncAddOrUpdate(BaseGroup group); void syncAddOrUpdate(SyncGroupDTO group);
void addOrUpdateSchool(BaseGroup group); void addOrUpdateSchool(SyncGroupDTO group);
} }
@@ -1,8 +1,7 @@
package com.yunhai.bschclassmanage.service; 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.DeleteUser;
import com.yunhai.bschclassmanage.model.tool.SyncUserDTO;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.List; import java.util.List;
@@ -10,7 +9,7 @@ import java.util.List;
@Service @Service
public interface ISyncUserService { public interface ISyncUserService {
void syncAddOrUpdate(List<BaseUser> userList); void syncAddOrUpdate(List<SyncUserDTO> userList);
void deleteUser(List<DeleteUser> deleteUserList); void deleteUser(List<DeleteUser> deleteUserList);
} }
@@ -5,119 +5,98 @@ import com.yunhai.bschclassmanage.entity.*;
import com.yunhai.bschclassmanage.enums.SchType; import com.yunhai.bschclassmanage.enums.SchType;
import com.yunhai.bschclassmanage.model.tool.YearTerm; import com.yunhai.bschclassmanage.model.tool.YearTerm;
import com.yunhai.bschclassmanage.service.*; import com.yunhai.bschclassmanage.service.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.*; import java.util.*;
import java.util.stream.Collectors;
@Slf4j @Slf4j
@Service @Service
@RequiredArgsConstructor
public class SchClassUpgradeService { public class SchClassUpgradeService {
@Autowired private final SchClassCacheService schClassCacheService;
private SchClassCacheService schClassCacheService; private final SchClassRepository schClassRepository;
private final ISchBinSchsystemService schBinSchsystemService;
@Autowired private final IBaseSchsystemGradeService baseSchsystemGradeService;
private SchClassQueryService queryService; private final IBaseYearTermService baseYearTermService;
private final UpgradeLogRepository upgradeLogRepository;
@Autowired private final JdbcTemplate jdbcTemplate;
private SchClassRepository schClassRepository;
@Autowired
private ISchBinSchsystemService schBinSchsystemService;
@Autowired
private IBaseSchsystemGradeService baseSchsystemGradeService;
@Autowired
private IBaseYearTermService baseYearTermService;
@Autowired
private UpgradeLogRepository upgradeLogRepository;
@Autowired
private JdbcTemplate jdbcTemplate;
public void upgradeAllClasses() { public void upgradeAllClasses() {
Date now = new Date(); Date now = new Date();
// 获取当前学年学期 // 获取当前学年学期
YearTerm yearTerm = baseYearTermService.getCurrentYearTerm(); YearTerm yearTerm = baseYearTermService.getCurrentYearTerm();
if (yearTerm == null) {
log.error("当前学年学期未配置,升级中止");
return;
}
int currentYear = yearTerm.getYear(); int currentYear = yearTerm.getYear();
List<Integer> schoolIds = schBinSchsystemService.getSchIds(); List<Integer> schoolIds = schBinSchsystemService.getSchIds();
for (Integer schId : schoolIds) { for (Integer schId : schoolIds) {
// ----- 2. 获取该校所有学制 ----- // 学校类型在整个学校维度内不变,提升到学制循环外只查一次
List<Integer> schsystemIds = schBinSchsystemService.getSchBinSchsystemIds(schId);
Integer schType = schBinSchsystemService.getSchType(schId); Integer schType = schBinSchsystemService.getSchType(schId);
// ----- 获取该校所有学制 -----
List<Integer> schsystemIds = schBinSchsystemService.getSchBinSchsystemIds(schId);
for (Integer sysId : schsystemIds) { for (Integer sysId : schsystemIds) {
Boolean status=true; // 已成功升级过的(学年+学制)直接跳过,避免重复升级
if (upgradeLogRepository.existsBySchIdAndSysIdAndYearAndStatus(schId, sysId, currentYear,status)) { if (upgradeLogRepository.existsBySchIdAndSysIdAndYearAndStatus(schId, sysId, currentYear, true)) {
log.info("学校{}学制{}学年{}学期已升级,跳过", schId, sysId, currentYear); log.info("学校{}学制{}学年{}已成功升级,跳过", schId, sysId, currentYear);
continue; // 直接返回,不做任何操作 continue;
} }
boolean success = true;
try { try {
upgradeSingleSystem(schId, schType, sysId, now); upgradeSingleSystem(schId, schType, sysId, now);
log.info("学校{} 学制{} 学年{} 升级执行成功", schId, sysId, currentYear); log.info("学校{} 学制{} 学年{} 升级执行成功", schId, sysId, currentYear);
} catch (Exception e) { } catch (Exception e) {
success = false;
log.error("学校{}学制{}升级失败,已跳过,不影响其他学制", schId, sysId, e); 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.clearClassCache(schId);
schClassCacheService.clearGradeateClassCache(schId);
} }
} }
//升级业务逻辑 //升级业务逻辑
@Transactional(propagation = Propagation.REQUIRES_NEW) @Transactional(propagation = Propagation.REQUIRES_NEW)
protected void upgradeSingleSystem(Integer schId, Integer schType, Integer sysId, Date now) { protected void upgradeSingleSystem(Integer schId, Integer schType, Integer sysId, Date now) {
// 1. 检查是否已升级(利用数据库唯一索引防并发) // 1. 获取该学制下的年级定义(轻量、带缓存),得到年级集合与最高年级
// 2. 执行升级逻辑
// 3. 插入升级日志
Map<Integer, String> gradeMap = baseSchsystemGradeService.getGradeNameMap(schId, sysId, null); Map<Integer, String> gradeMap = baseSchsystemGradeService.getGradeNameMap(schId, sysId, null);
if (gradeMap == null || gradeMap.isEmpty()) {
return;
}
List<Integer> gradeIds = new ArrayList<>(gradeMap.keySet()); List<Integer> gradeIds = new ArrayList<>(gradeMap.keySet());
Collections.sort(gradeIds); Collections.sort(gradeIds);
if (gradeIds.isEmpty()) return;
Integer maxGrade = gradeIds.get(gradeIds.size() - 1); Integer maxGrade = gradeIds.get(gradeIds.size() - 1);
// ----- 3. 升级非最高年级 ----- // 2. 非最高年级:基础教育整体升一级(SET 语义,单语句原子完成)
for (Integer gradeId : gradeIds) { if (SchType.BASICEDUCATION.getKey().equals(schType)) {
if (gradeId.equals(maxGrade)) continue; List<Integer> nonMaxGrades = new ArrayList<>(gradeIds.subList(0, gradeIds.size() - 1));
List<SchClass> classList = queryService.getSchClassCacheFilter(schId, gradeId, null); if (!nonMaxGrades.isEmpty()) {
if (classList.isEmpty()) continue; int updated = schClassRepository.batchUpgradeGrade(schId, nonMaxGrades, now);
log.info("学校{}学制{} 非最高年级升一级,影响班级数={}", schId, sysId, updated);
// 执行升级
for (SchClass clazz : classList) {
if(schType.equals(SchType.BASICEDUCATION.getKey())) {
clazz.setGradeId(gradeId + 1);//基础教育年级+1
} }
clazz.setUpdateDate(now);
clazz.setUpdateUserId(0);
}
schClassRepository.saveAll(classList);
}
// ----- 4. 处理最高年级(毕业) -----
List<SchClass> 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 {
} }
} }
@@ -4,6 +4,7 @@ import com.yunhai.bschclassmanage.Repository.SchInfoRepository;
import com.yunhai.bschclassmanage.entity.SchInfo; import com.yunhai.bschclassmanage.entity.SchInfo;
import com.yunhai.bschclassmanage.enums.NodeType; import com.yunhai.bschclassmanage.enums.NodeType;
import com.yunhai.bschclassmanage.model.tool.BaseGroup; import com.yunhai.bschclassmanage.model.tool.BaseGroup;
import com.yunhai.bschclassmanage.model.tool.SyncGroupDTO;
import com.yunhai.bschclassmanage.service.ISyncGroupService; import com.yunhai.bschclassmanage.service.ISyncGroupService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -19,7 +20,7 @@ public class SyncGroupService implements ISyncGroupService {
private SchInfoRepository schInfoRepository; private SchInfoRepository schInfoRepository;
public void syncAddOrUpdate(BaseGroup group) { public void syncAddOrUpdate(SyncGroupDTO group) {
//学校组 年级组 班级组 //学校组 年级组 班级组
if(group.getType().equals(NodeType.SCHOOL.getId())){ 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<SchInfo> schOpt=schInfoRepository.findBySchId(group.getId()); Optional<SchInfo> schOpt=schInfoRepository.findBySchId(group.getId());
if(schOpt.isPresent()){ if(schOpt.isPresent()){
@@ -1,33 +1,31 @@
package com.yunhai.bschclassmanage.service.impl.sync; package com.yunhai.bschclassmanage.service.impl.sync;
import com.yunhai.bschclassmanage.Repository.SchInfoRepository;
import com.yunhai.bschclassmanage.Repository.SchStudentRepository; import com.yunhai.bschclassmanage.Repository.SchStudentRepository;
import com.yunhai.bschclassmanage.Repository.SchTeacherGroupRepository; import com.yunhai.bschclassmanage.Repository.SchTeacherGroupRepository;
import com.yunhai.bschclassmanage.Repository.SchTeacherRepository; import com.yunhai.bschclassmanage.Repository.SchTeacherRepository;
import com.yunhai.bschclassmanage.entity.SchInfo;
import com.yunhai.bschclassmanage.entity.SchStudent; import com.yunhai.bschclassmanage.entity.SchStudent;
import com.yunhai.bschclassmanage.entity.SchTeacher; import com.yunhai.bschclassmanage.entity.SchTeacher;
import com.yunhai.bschclassmanage.entity.SchTeacherGroup; import com.yunhai.bschclassmanage.entity.SchTeacherGroup;
import com.yunhai.bschclassmanage.enums.NodeType;
import com.yunhai.bschclassmanage.enums.StuAbnormalStatus; import com.yunhai.bschclassmanage.enums.StuAbnormalStatus;
import com.yunhai.bschclassmanage.enums.TransferStatus; import com.yunhai.bschclassmanage.enums.TransferStatus;
import com.yunhai.bschclassmanage.enums.UserRole; 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.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.ISyncUserService;
import com.yunhai.bschclassmanage.service.impl.student.SchStudentCacheService; import com.yunhai.bschclassmanage.service.impl.student.SchStudentCacheService;
import com.yunhai.bschclassmanage.service.impl.teacher.SchTeacherCacheService; import com.yunhai.bschclassmanage.service.impl.teacher.SchTeacherCacheService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.*; import java.util.*;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Slf4j
@Service @Service
public class SyncUserService implements ISyncUserService { public class SyncUserService implements ISyncUserService {
@@ -47,18 +45,25 @@ public class SyncUserService implements ISyncUserService {
private SchTeacherCacheService schTeacherCacheService; private SchTeacherCacheService schTeacherCacheService;
// ==================== 同步入口 ==================== // ==================== 同步入口 ====================
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void syncAddOrUpdate(List<BaseUser> userList) { public void syncAddOrUpdate(List<SyncUserDTO> userList) {
if (userList == null || userList.isEmpty()) return; if (userList == null || userList.isEmpty()) return;
// 按角色分组 // 按角色分组(每个用户只遍历一次 roleList)
List<BaseUser> syncStudentList = new ArrayList<>(); List<SyncUserDTO> syncStudentList = new ArrayList<>();
List<BaseUser> syncTeacherList = new ArrayList<>(); List<SyncUserDTO> syncTeacherList = new ArrayList<>();
for (BaseUser user : userList) { for (SyncUserDTO user : userList) {
if (isStudent(user)) syncStudentList.add(user); Set<Integer> roleIds = collectRoleIds(user);
if (isTeacher(user)) syncTeacherList.add(user); if (isStudent(roleIds)) {
syncStudentList.add(user);
} else if (isTeacher(roleIds)) {
syncTeacherList.add(user);
} else {
log.error("用户:{} 角色不符合同步要求!", user.getAccount());
}
} }
Date now = new Date(); Date now = new Date();
@@ -71,124 +76,112 @@ public class SyncUserService implements ISyncUserService {
} }
} }
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void deleteUser(List<DeleteUser> deleteUserList) { public void deleteUser(List<DeleteUser> deleteUserList) {
if (deleteUserList == null || deleteUserList.isEmpty()) return; if (deleteUserList == null || deleteUserList.isEmpty()) return;
// 提取所有 userId // 过滤出有效的 (userId, groupId) 条目,仅计算一次
List<Integer> allUserIds = deleteUserList.stream() List<DeleteUser> validDeletes = deleteUserList.stream()
.map(DeleteUser::getUserId)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
if (allUserIds.isEmpty()) return;
// ---------- 1. 物理删除学生 ----------
List<DeleteUser> studentDeletes = deleteUserList.stream()
.filter(du -> du.getUserId() != null && du.getGroupId() != null) .filter(du -> du.getUserId() != null && du.getGroupId() != null)
.collect(Collectors.toList()); .collect(Collectors.toList());
if (!studentDeletes.isEmpty()) { if (validDeletes.isEmpty()) return;
// 批量查询学生
List<SchStudent> allStudents = schStudentRepository.findByStuIdIn(allUserIds); List<Integer> allUserIds = validDeletes.stream()
Map<String, SchStudent> studentMap = allStudents.stream() .map(DeleteUser::getUserId)
.distinct()
.collect(Collectors.toList());
// ---------- 1. 物理删除学生 ----------
List<SchStudent> toDeleteStudents = new ArrayList<>();
Map<String, SchStudent> studentMap = schStudentRepository.findByStuIdIn(allUserIds).stream()
.collect(Collectors.toMap( .collect(Collectors.toMap(
s -> buildStudentKey(s.getStuId(), s.getGroupId()), s -> buildStudentKey(s.getStuId(), s.getGroupId()),
Function.identity() Function.identity()
)); ));
List<SchStudent> toDelete = new ArrayList<>(); for (DeleteUser du : validDeletes) {
for (DeleteUser du : studentDeletes) { SchStudent stu = studentMap.get(buildStudentKey(du.getUserId(), du.getGroupId()));
String key = buildStudentKey(du.getUserId(), du.getGroupId());
SchStudent stu = studentMap.get(key);
if (stu != null) { if (stu != null) {
toDelete.add(stu); toDeleteStudents.add(stu);
} }
} }
if (!toDelete.isEmpty()) { if (!toDeleteStudents.isEmpty()) {
// 先清除缓存(需要 schId,可从学生实体获取或查询) // 按 (schId, classId) 去重后清除缓存
for (SchStudent stu : toDelete) { Map<Integer, Set<Integer>> classKeys = new HashMap<>();
// 假设 SchStudent 有 schId 字段,若没有则需通过 groupId 查询 for (SchStudent stu : toDeleteStudents) {
Integer schId = stu.getSchId(); // 若没有,调用 service 查询 if (stu.getSchId() != null && stu.getGroupId() != null) {
if (schId != null) { classKeys.computeIfAbsent(stu.getSchId(), k -> new HashSet<>()).add(stu.getGroupId());
schStudentCacheService.clearClassStudentCache(schId, stu.getGroupId());
} }
} }
// 物理删除 classKeys.forEach((schId, classIds) -> schStudentCacheService.clearClassStudentCaches(schId, classIds));
schStudentRepository.deleteAll(toDelete); schStudentRepository.deleteAll(toDeleteStudents);
}
} }
// ---------- 2. 物理删除教师分组(不删教师主体) ---------- // ---------- 2. 物理删除教师分组(不删教师主体) ----------
List<DeleteUser> teacherDeletes = deleteUserList.stream() // 同一 (teaId, groupId) 可能跨多所学校,用 List 收集避免重复键被丢弃
.filter(du -> du.getUserId() != null && du.getGroupId() != null) Map<String, List<SchTeacherGroup>> groupMap = schTeacherGroupRepository.findByTeaIdIn(allUserIds).stream()
.collect(Collectors.toList()); .collect(Collectors.groupingBy(g -> buildTeacherGroupKey(g.getTeaId(), g.getGroupId())));
if (!teacherDeletes.isEmpty()) {
// 查询所有涉及的分组
List<SchTeacherGroup> allGroups = schTeacherGroupRepository.findByTeaIdIn(allUserIds);
Map<String, SchTeacherGroup> groupMap = allGroups.stream()
.collect(Collectors.toMap(
g -> buildTeacherGroupKey(g.getTeaId(), g.getGroupId()),
Function.identity()
));
List<SchTeacherGroup> toDeleteGroups = new ArrayList<>(); List<SchTeacherGroup> toDeleteGroups = new ArrayList<>();
for (DeleteUser du : teacherDeletes) { for (DeleteUser du : validDeletes) {
String key = buildTeacherGroupKey(du.getUserId(), du.getGroupId()); List<SchTeacherGroup> groups = groupMap.get(buildTeacherGroupKey(du.getUserId(), du.getGroupId()));
SchTeacherGroup group = groupMap.get(key); if (groups != null) {
if (group != null) { toDeleteGroups.addAll(groups);
toDeleteGroups.add(group);
} }
} }
if (!toDeleteGroups.isEmpty()) { if (!toDeleteGroups.isEmpty()) {
// 按学校维度分组,用于缓存清除和剩余分组检查 // 按学校维度去重后清除缓存
Map<Integer, List<SchTeacherGroup>> groupsBySchool = toDeleteGroups.stream() Set<Integer> schIds = toDeleteGroups.stream()
.collect(Collectors.groupingBy(SchTeacherGroup::getSchId)); .map(SchTeacherGroup::getSchId)
.filter(Objects::nonNull)
// 先清除缓存(针对每个分组所属学校) .collect(Collectors.toSet());
for (SchTeacherGroup group : toDeleteGroups) { schTeacherCacheService.clearTeacherCaches(schIds);
schTeacherCacheService.clearTeacherCache(group.getSchId());
}
// 物理删除分组
schTeacherGroupRepository.deleteAll(toDeleteGroups); schTeacherGroupRepository.deleteAll(toDeleteGroups);
}
} }
} }
// ==================== 角色判断 ==================== // ==================== 角色判断 ====================
private boolean isStudent(BaseUser user) {
if (user.getRoleList() == null) return false; /**
* 提取用户的角色 id 集合(去重、过滤空值),每个用户仅遍历一次。
*/
private Set<Integer> collectRoleIds(SyncUserDTO user) {
if (user.getRoleList() == null) return Collections.emptySet();
return user.getRoleList().stream() 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) { private boolean isStudent(Set<Integer> roleIds) {
if (user.getRoleList() == null) return false; return roleIds.contains(UserRole.STU_USER.getValue());
return user.getRoleList().stream() }
.anyMatch(r -> r.getRoleId() != null &&
(r.getRoleId().equals(UserRole.TEACHER_USER.getValue()) || private boolean isTeacher(Set<Integer> roleIds) {
r.getRoleId().equals(UserRole.SCH_LEADER_USER.getValue()))); return roleIds.contains(UserRole.TEACHER_USER.getValue())
|| roleIds.contains(UserRole.SCH_LEADER_USER.getValue());
} }
// ==================== 学生同步 ==================== // ==================== 学生同步 ====================
private void syncStudents(List<BaseUser> studentUsers, Date now) {
List<Integer> stuIds = studentUsers.stream().map(BaseUser::getId).distinct().collect(Collectors.toList()); private void syncStudents(List<SyncUserDTO> studentUsers, Date now) {
List<Integer> stuIds = studentUsers.stream().map(SyncUserDTO::getId).distinct().collect(Collectors.toList());
// 已有学生记录 (stuId, groupId) -> SchStudent // 已有学生记录 (stuId, groupId) -> SchStudent
Map<String, SchStudent> studentMap = schStudentRepository.findByStuIdIn(stuIds) Map<String, SchStudent> studentMap = schStudentRepository.findByStuIdIn(stuIds).stream()
.stream()
.collect(Collectors.toMap( .collect(Collectors.toMap(
s -> buildStudentKey(s.getStuId(), s.getGroupId()), s -> buildStudentKey(s.getStuId(), s.getGroupId()),
Function.identity() Function.identity()
)); ));
List<SchStudent> saveList = new ArrayList<>(); List<SchStudent> saveList = new ArrayList<>();
// 受影响班级缓存键 (schId -> classId 集合),用于同步后失效
Map<Integer, Set<Integer>> classKeys = new HashMap<>();
for (BaseUser user : studentUsers) { for (SyncUserDTO user : studentUsers) {
if (user.getGroups() == null) continue; if (user.getGroups() == null) continue;
for (BaseUser.GroupInfo group : user.getGroups()) {
for (SyncUserGroupDTO group : user.getGroups()) {
Integer groupId = group.getGroupId(); Integer groupId = group.getGroupId();
Integer schId = group.getSchoolId(); Integer schId = group.getSchoolId();
if (groupId == null || schId == null) continue; if (groupId == null || schId == null) continue;
@@ -201,21 +194,18 @@ public class SyncUserService implements ISyncUserService {
student = createStudent(user, group, now); student = createStudent(user, group, now);
} }
saveList.add(student); saveList.add(student);
classKeys.computeIfAbsent(schId, k -> new HashSet<>()).add(groupId);
} }
} }
if (!saveList.isEmpty()) { if (!saveList.isEmpty()) {
schStudentRepository.saveAll(saveList); schStudentRepository.saveAll(saveList);
// 缓存:将学生加入班级缓存(Set结构) // 同步后使受影响班级的学生缓存失效,避免读到旧数据
// for (SchStudent stu : saveList) { classKeys.forEach((schId, classIds) -> schStudentCacheService.clearClassStudentCaches(schId, classIds));
// if (stu.getSchId() != null && stu.getGroupId() != null) {
// schStudentCacheService.addClassStudentCache(stu.getSchId(), stu.getGroupId(), stu.getStuId());
// }
// }
} }
} }
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.setAccount(user.getAccount());
student.setName(user.getName()); student.setName(user.getName());
student.setGender(user.getGender()); student.setGender(user.getGender());
@@ -228,7 +218,7 @@ public class SyncUserService implements ISyncUserService {
student.setUpdateDate(now); 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(); SchStudent student = new SchStudent();
student.setStuId(user.getId()); student.setStuId(user.getId());
student.setAccount(user.getAccount()); student.setAccount(user.getAccount());
@@ -248,17 +238,16 @@ public class SyncUserService implements ISyncUserService {
} }
// ==================== 教师同步 ==================== // ==================== 教师同步 ====================
private void syncTeachers(List<BaseUser> teacherUsers, Date now) {
List<Integer> teaIds = teacherUsers.stream().map(BaseUser::getId).distinct().collect(Collectors.toList()); private void syncTeachers(List<SyncUserDTO> teacherUsers, Date now) {
List<Integer> teaIds = teacherUsers.stream().map(SyncUserDTO::getId).distinct().collect(Collectors.toList());
// 教师主体(唯一) // 教师主体(唯一)
Map<Integer, SchTeacher> teacherMap = schTeacherRepository.findByTeaIdIn(teaIds) Map<Integer, SchTeacher> teacherMap = schTeacherRepository.findByTeaIdIn(teaIds).stream()
.stream()
.collect(Collectors.toMap(SchTeacher::getTeaId, Function.identity())); .collect(Collectors.toMap(SchTeacher::getTeaId, Function.identity()));
// 教师分组 (teaId, groupId, schId) -> SchTeacherGroup // 教师分组 (teaId, groupId, schId) -> SchTeacherGroup
Map<String, SchTeacherGroup> groupMap = schTeacherGroupRepository.findByTeaIdIn(teaIds) Map<String, SchTeacherGroup> groupMap = schTeacherGroupRepository.findByTeaIdIn(teaIds).stream()
.stream()
.collect(Collectors.toMap( .collect(Collectors.toMap(
g -> buildTeacherGroupKeyWithSchId(g.getTeaId(), g.getGroupId(), g.getSchId()), g -> buildTeacherGroupKeyWithSchId(g.getTeaId(), g.getGroupId(), g.getSchId()),
Function.identity() Function.identity()
@@ -266,8 +255,10 @@ public class SyncUserService implements ISyncUserService {
List<SchTeacher> saveTeacherList = new ArrayList<>(); List<SchTeacher> saveTeacherList = new ArrayList<>();
List<SchTeacherGroup> saveGroupList = new ArrayList<>(); List<SchTeacherGroup> saveGroupList = new ArrayList<>();
// 受影响学校缓存键,用于同步后失效
Set<Integer> schIds = new HashSet<>();
for (BaseUser user : teacherUsers) { for (SyncUserDTO user : teacherUsers) {
// 教师主体(只存一条) // 教师主体(只存一条)
SchTeacher teacher = teacherMap.get(user.getId()); SchTeacher teacher = teacherMap.get(user.getId());
if (teacher != null) { if (teacher != null) {
@@ -279,7 +270,7 @@ public class SyncUserService implements ISyncUserService {
// 教师分组(遍历 groups) // 教师分组(遍历 groups)
if (user.getGroups() == null) continue; if (user.getGroups() == null) continue;
for (BaseUser.GroupInfo group : user.getGroups()) { for (SyncUserGroupDTO group : user.getGroups()) {
Integer groupId = group.getGroupId(); Integer groupId = group.getGroupId();
Integer schId = group.getSchoolId(); Integer schId = group.getSchoolId();
if (groupId == null || schId == null) continue; if (groupId == null || schId == null) continue;
@@ -293,6 +284,7 @@ public class SyncUserService implements ISyncUserService {
tg = createTeacherGroup(user, group, now); tg = createTeacherGroup(user, group, now);
} }
saveGroupList.add(tg); saveGroupList.add(tg);
schIds.add(schId);
} }
} }
@@ -301,18 +293,14 @@ public class SyncUserService implements ISyncUserService {
} }
if (!saveGroupList.isEmpty()) { if (!saveGroupList.isEmpty()) {
schTeacherGroupRepository.saveAll(saveGroupList); schTeacherGroupRepository.saveAll(saveGroupList);
// // 缓存:更新教师缓存(学校维度) }
// // 为简单起见,清除每个涉及的学校缓存,然后重新添加该学校所有教师(但这里我们只添加本次更新的教师,因为Set可重复) // 同步后使受影响学校的教师缓存失效
// // 更稳健:清除后重建,但为了减少数据库查询,我们使用 add 方法 if (!schIds.isEmpty()) {
// for (SchTeacherGroup g : saveGroupList) { schTeacherCacheService.clearTeacherCaches(schIds);
// if (g.getSchId() != null) {
// schTeacherCacheService.addSchoolTeacherCache(g.getSchId(), g.getTeaId());
// }
// }
} }
} }
private void updateTeacher(SchTeacher teacher, BaseUser user, Date now) { private void updateTeacher(SchTeacher teacher, SyncUserDTO user, Date now) {
teacher.setAccount(user.getAccount()); teacher.setAccount(user.getAccount());
teacher.setName(user.getName()); teacher.setName(user.getName());
teacher.setGender(user.getGender()); teacher.setGender(user.getGender());
@@ -322,7 +310,7 @@ public class SyncUserService implements ISyncUserService {
teacher.setUpdateDate(now); teacher.setUpdateDate(now);
} }
private SchTeacher createTeacher(BaseUser user, Date now) { private SchTeacher createTeacher(SyncUserDTO user, Date now) {
SchTeacher teacher = new SchTeacher(); SchTeacher teacher = new SchTeacher();
teacher.setTeaId(user.getId()); teacher.setTeaId(user.getId());
teacher.setAccount(user.getAccount()); teacher.setAccount(user.getAccount());
@@ -336,7 +324,7 @@ public class SyncUserService implements ISyncUserService {
return teacher; 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(); SchTeacherGroup tg = new SchTeacherGroup();
tg.setTeaId(user.getId()); tg.setTeaId(user.getId());
tg.setGroupId(group.getGroupId()); tg.setGroupId(group.getGroupId());
@@ -346,6 +334,7 @@ public class SyncUserService implements ISyncUserService {
} }
// ==================== 辅助键生成 ==================== // ==================== 辅助键生成 ====================
private String buildStudentKey(Integer stuId, Integer groupId) { private String buildStudentKey(Integer stuId, Integer groupId) {
return stuId + "_" + groupId; return stuId + "_" + groupId;
} }
@@ -263,12 +263,16 @@ public class OtherUtil {
return null; return null;
} }
try { try {
// 使用 TypeReference 保留泛型信息,避免类型擦除 // 注意:new TypeReference<List<T>>() {} 会因泛型擦除退化为原始 List,
return objectMapper.readValue(json, new TypeReference<List<T>>() {}); // 元素被反序列化为 LinkedHashMap,后续强转目标类型会抛 ClassCastException。
// 必须用 TypeFactory 动态构建 List<T> 的 JavaType 才能保留元素类型。
JavaType listType = objectMapper.getTypeFactory()
.constructCollectionType(List.class, targetClass);
return objectMapper.readValue(json, listType);
} catch (Exception e) { } catch (Exception e) {
System.err.println("解析 JSON 数组失败,json 前缀: " + json); System.err.println("解析 JSON 数组失败,json 前缀: " + json);
e.printStackTrace(); e.printStackTrace();
return null; throw new DException("解析 JSON 数组失败!");
} }
} }
@@ -286,9 +290,9 @@ public class OtherUtil {
try { try {
return objectMapper.readValue(json, targetClass); return objectMapper.readValue(json, targetClass);
} catch (Exception e) { } catch (Exception e) {
System.err.println("解析 JSON 数组失败,json 前缀: " + json); System.err.println("解析 JSON 对象失败,json 前缀: " + json);
e.printStackTrace(); e.printStackTrace();
return null; throw new DException("解析 JSON 对象失败!");
} }
} }
@@ -305,7 +309,7 @@ public class OtherUtil {
} catch (Exception e) { } catch (Exception e) {
System.err.println("解析 JSON 数组失败: " + json); System.err.println("解析 JSON 数组失败: " + json);
e.printStackTrace(); e.printStackTrace();
return null; throw new DException("解析 JSON 数组失败!");
} }
} }
@@ -320,7 +324,7 @@ public class OtherUtil {
} catch (Exception e) { } catch (Exception e) {
System.err.println("解析 JSON 数组失败: " + json); System.err.println("解析 JSON 数组失败: " + json);
e.printStackTrace(); e.printStackTrace();
return null; throw new DException("解析 JSON 数组失败!");
} }
} }