@@ -0,0 +1,40 @@
|
||||
package com.yunhai.bschclassmanage.model.vo.sch;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.yunhai.bschclassmanage.model.vo.SelectIntVO;
|
||||
import com.yunhai.bschclassmanage.model.vo.SelectStrVO;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 教师分组信息(oapi/sch/getTeaCustomGroupList 输出,对应教师分组管理页面的组/组员/组长三栏)
|
||||
*/
|
||||
@Data
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class OapiGroupVO {
|
||||
|
||||
/** 组ID(sch_teacher_custom_group.id) */
|
||||
private Integer groupId;
|
||||
|
||||
/** 组名称 */
|
||||
private String groupName;
|
||||
|
||||
/** 组类型(1年级组 2教研组 3备课组) */
|
||||
private Integer groupType;
|
||||
|
||||
/** 组类型名称 */
|
||||
private String groupTypeName;
|
||||
|
||||
/** 学科集合 [{id:学科编码, name:学科名称}] */
|
||||
private List<SelectStrVO> subjects;
|
||||
|
||||
/** 年级集合 [{id:年级编码, name:年级名称}] */
|
||||
private List<SelectIntVO> grades;
|
||||
|
||||
/** 组员集合 [{id:教师id, name:教师名称}](不含组长) */
|
||||
private List<SelectIntVO> members;
|
||||
|
||||
/** 组长集合 [{id:教师id, name:教师名称}] */
|
||||
private List<SelectIntVO> leaders;
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package com.yunhai.bschclassmanage;
|
||||
|
||||
import com.yunhai.bschclassmanage.service.IStudentClassHistoryService;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
/**
|
||||
* 本地复现 api/history/getHistoryClassTeachByStuId?stuId=10068 的内部服务错误。
|
||||
* 直接调用 service 层(绕过 SSO 过滤器),打印真实异常堆栈。
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
|
||||
class ReproduceHistoryTeachByStuIdTest {
|
||||
|
||||
@Autowired
|
||||
private IStudentClassHistoryService studentClassHistoryService;
|
||||
|
||||
@Test
|
||||
void reproduce() {
|
||||
try {
|
||||
// 模拟真实 HTTP 场景:UserContextInterceptor.resolveUser 恒返回 null,schId 为 null
|
||||
Object result = studentClassHistoryService.getHistoryClassTeachByStuId(null, 10068);
|
||||
System.out.println("=== SUCCESS (schId=null) ===");
|
||||
System.out.println(result);
|
||||
} catch (Throwable e) {
|
||||
System.out.println("=== EXCEPTION (schId=null): " + e.getClass().getName() + " ===");
|
||||
System.out.println("message: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private com.yunhai.bschclassmanage.utils.RedisUtil redisUtil;
|
||||
|
||||
@Autowired
|
||||
private com.yunhai.bschclassmanage.Repository.SchStudentRepository schStudentRepository;
|
||||
|
||||
/**
|
||||
* 回归用例:同一 stu_id 在 sch_student 存在多行(学生在多所学校各有一条就学记录)。
|
||||
* 修复前 findByStuId 返回 Optional,会抛 NonUniqueResultException(线上表现为 code 6 内部服务错误)。
|
||||
* 修复后应取当前学校那一行正常返回。@Transactional 保证测试数据回滚。
|
||||
*/
|
||||
@org.springframework.transaction.annotation.Transactional
|
||||
@Test
|
||||
void duplicateStuIdShouldNotCrash() {
|
||||
// 本地库里 stu_id=10068 已有一行(sch_id=3, class_id=298),再造一行别校的记录
|
||||
com.yunhai.bschclassmanage.entity.SchStudent other = new com.yunhai.bschclassmanage.entity.SchStudent();
|
||||
other.setStuId(10068);
|
||||
other.setSchId(999);
|
||||
other.setClassId(888);
|
||||
other.setName("重复记录-别校");
|
||||
schStudentRepository.save(other);
|
||||
schStudentRepository.flush();
|
||||
|
||||
// 当前学校 schId=3 的记录应被正确选中,不再抛 NonUniqueResultException
|
||||
Object result = studentClassHistoryService.getHistoryClassTeachByStuId(3, 10068);
|
||||
System.out.println("=== DUPLICATE stu_id RESULT ===");
|
||||
System.out.println(result);
|
||||
if (result == null) {
|
||||
throw new AssertionError("expected non-null result");
|
||||
}
|
||||
|
||||
// 换成重复行所在学校 schId=999:实时表命中但班级不存在 → 应返回业务异常而不是内部错误
|
||||
try {
|
||||
studentClassHistoryService.getHistoryClassTeachByStuId(999, 10068);
|
||||
System.out.println("=== schId=999 unexpectedly succeeded ===");
|
||||
} catch (com.yunhai.platform.utils.exception.DException e) {
|
||||
System.out.println("=== schId=999 DException (expected): " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void poisonCacheRead() {
|
||||
for (String key : new String[]{
|
||||
"bsch.classmanage.system_yearterm_poison",
|
||||
"bsch.classmanage.system_yearterm_poison2"}) {
|
||||
try {
|
||||
Object raw = redisUtil.get(key, Object.class);
|
||||
System.out.println("=== RAW GET [" + key + "]: " + (raw == null ? "null" : raw.getClass().getName()));
|
||||
} catch (Throwable e) {
|
||||
System.out.println("=== RAW GET EXCEPTION [" + key + "]: " + e.getClass().getName() + " ===");
|
||||
System.out.println("message: " + e.getMessage());
|
||||
}
|
||||
try {
|
||||
Object list = redisUtil.getList(key, com.yunhai.bschclassmanage.entity.BaseYearTerm.class);
|
||||
System.out.println("=== GET LIST [" + key + "]: " + list);
|
||||
} catch (Throwable e) {
|
||||
System.out.println("=== GET LIST EXCEPTION [" + key + "]: " + e.getClass().getName() + " ===");
|
||||
System.out.println("message: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void reproduceWithSchId() {
|
||||
try {
|
||||
Object result = studentClassHistoryService.getHistoryClassTeachByStuId(3, 10068);
|
||||
System.out.println("=== SUCCESS (schId=3) ===");
|
||||
System.out.println(result);
|
||||
} catch (Throwable e) {
|
||||
System.out.println("=== EXCEPTION (schId=3): " + e.getClass().getName() + " ===");
|
||||
System.out.println("message: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user