@@ -52,8 +52,10 @@ public class ControllerUserContextAspect {
|
||||
} else if (devContextEnabled) {
|
||||
info.userId = 99999;
|
||||
info.userName = "本地开发";
|
||||
info.schId = 1;
|
||||
log.debug("[UserContext] 无 SSO 会话,已使用本地开发身份(schId=1)");
|
||||
// schId=3(云海中学/李晶校):班级数据已切平台接口,dev 校须在平台有班级数据才能联调绑定班级页;
|
||||
// userId 保持 99999(平台查无此用户 → GetUserMod 走本地兜底菜单,不回真实模块树)
|
||||
info.schId = 3;
|
||||
log.debug("[UserContext] 无 SSO 会话,已使用本地开发身份(schId=3)");
|
||||
}
|
||||
info.ip = clientIp();
|
||||
UserContext.set(info);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.yunhai.acaclassroom.controller.api;
|
||||
|
||||
import com.yunhai.acaclassroom.annotation.OperationLog;
|
||||
import com.yunhai.acaclassroom.config.Config;
|
||||
import com.yunhai.acaclassroom.service.IClassService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -10,7 +9,8 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 班级档案接口(class/,绑定班级页数据源)。
|
||||
* 班级接口(class/,绑定班级页数据源)。
|
||||
* 班级数据实时取自班级管理(bschclass)开放接口,本应用不落库、不提供增删。
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping(value = Config.API_PATH + "/class/")
|
||||
@@ -19,25 +19,9 @@ public class ClassController {
|
||||
@Autowired
|
||||
private IClassService classService;
|
||||
|
||||
/** 班级列表(全校,可按年级过滤;行含 classroomId=绑定的教室 id,未绑定为 null)。 */
|
||||
/** 班级列表(全校高中学段,可按年级过滤;行含 classroomId=绑定的教室 id,未绑定为 null)。 */
|
||||
@GetMapping("List")
|
||||
public List<Map<String, Object>> list(@RequestParam(value = "grade", required = false) Integer grade) {
|
||||
return classService.list(grade);
|
||||
}
|
||||
|
||||
@PostMapping("Add")
|
||||
@OperationLog(module = "教室管理-绑定班级", type = "CREATE")
|
||||
public Integer add(@RequestParam("className") String className,
|
||||
@RequestParam("grade") Integer grade,
|
||||
@RequestParam(value = "studentCount", required = false) Integer studentCount,
|
||||
@RequestParam(value = "headTeacher", required = false) String headTeacher) {
|
||||
return classService.add(className, grade, studentCount, headTeacher);
|
||||
}
|
||||
|
||||
/** 删除班级(已被教室绑定时拒绝)。 */
|
||||
@PostMapping("Delete")
|
||||
@OperationLog(module = "教室管理-绑定班级", type = "DELETE")
|
||||
public void delete(@RequestParam("serid") Integer serid) {
|
||||
classService.delete(serid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
package com.yunhai.acaclassroom.entity;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Table;
|
||||
|
||||
/**
|
||||
* 班级档案(绑定班级页数据源)。班名校内唯一;教室经 aca_classroom.class_id 引用。
|
||||
*/
|
||||
@Entity
|
||||
@Getter
|
||||
@Setter
|
||||
@Table(name = "aca_class")
|
||||
public class SchoolClass extends BaseEntity {
|
||||
|
||||
/** 年级:1=高一 2=高二 3=高三 */
|
||||
public static final int GRADE_1 = 1;
|
||||
public static final int GRADE_2 = 2;
|
||||
public static final int GRADE_3 = 3;
|
||||
|
||||
/** 班级名称(如 高一1班),校内唯一 */
|
||||
@Column(name = "class_name", nullable = false, length = 50)
|
||||
private String className;
|
||||
|
||||
/** 年级:1=高一 2=高二 3=高三 */
|
||||
@Column(name = "grade", nullable = false)
|
||||
private Integer grade;
|
||||
|
||||
/** 学生人数 */
|
||||
@Column(name = "student_count", nullable = false)
|
||||
private Integer studentCount;
|
||||
|
||||
/** 班主任姓名 */
|
||||
@Column(name = "head_teacher", length = 50)
|
||||
private String headTeacher;
|
||||
|
||||
/** 所属学校 id(多租户) */
|
||||
@Column(name = "sch_id", nullable = false)
|
||||
private Integer schId;
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
package com.yunhai.acaclassroom.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 班级管理(bschclass)开放接口 {@code /oapi/sch/getSchClassList} 响应项。
|
||||
*
|
||||
* <p>id 体系:{@link #getId()} 是 sch_class 物理主键;{@link #getClassId()} 是分组体系
|
||||
* classId(学生树 GetStuTree 的班级节点 id 与之同源),本应用的教室绑定关系
|
||||
* {@code aca_classroom.class_id} 存它——与班级服务生态(学生树/任教)同一把钥匙。</p>
|
||||
*
|
||||
* <p>年级口径:平台 gradeId 为平台年级字典(10=高一 11=高二 12=高三;小学 1~6、
|
||||
* 初中 7~9、其他学段另有取值),本应用只有高中三个年级 Tab,
|
||||
* {@link #highGrade()} 把 10/11/12 映射为 1/2/3,其余返回 null(绑定页不展示)。</p>
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class PlatformSchoolClass {
|
||||
|
||||
/** 平台年级字典 → 本应用年级(1=高一 2=高二 3=高三)映射 */
|
||||
private static final Map<Integer, Integer> HIGH_GRADE_MAP = createHighGradeMap();
|
||||
|
||||
private static Map<Integer, Integer> createHighGradeMap() {
|
||||
Map<Integer, Integer> m = new HashMap<>();
|
||||
m.put(10, 1);
|
||||
m.put(11, 2);
|
||||
m.put(12, 3);
|
||||
return Collections.unmodifiableMap(m);
|
||||
}
|
||||
|
||||
/** sch_class 物理主键 */
|
||||
private Integer id;
|
||||
|
||||
/** 分组体系班级 id(学生树班级节点同源),绑定关系存此值 */
|
||||
private Integer classId;
|
||||
|
||||
/** 班级编码(如 202601) */
|
||||
private String code;
|
||||
|
||||
/** 班级名(如 1班,不含年级前缀) */
|
||||
private String name;
|
||||
|
||||
/** 平台年级字典 id(10=高一 11=高二 12=高三,小学/初中另有序列) */
|
||||
private Integer gradeId;
|
||||
|
||||
/** 年级名(如 高一) */
|
||||
private String gradeName;
|
||||
|
||||
private Integer schId;
|
||||
|
||||
private String schName;
|
||||
|
||||
/** 班主任(第一任课教师) */
|
||||
private Integer firstTeacherId;
|
||||
|
||||
private String firstTeacherName;
|
||||
|
||||
private Integer campusId;
|
||||
|
||||
private Integer enrollYear;
|
||||
|
||||
private Boolean graduate;
|
||||
|
||||
/** 校内展示序 */
|
||||
private Integer sort;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Integer getClassId() {
|
||||
return classId;
|
||||
}
|
||||
|
||||
public void setClassId(Integer classId) {
|
||||
this.classId = classId;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Integer getGradeId() {
|
||||
return gradeId;
|
||||
}
|
||||
|
||||
public void setGradeId(Integer gradeId) {
|
||||
this.gradeId = gradeId;
|
||||
}
|
||||
|
||||
public String getGradeName() {
|
||||
return gradeName;
|
||||
}
|
||||
|
||||
public void setGradeName(String gradeName) {
|
||||
this.gradeName = gradeName;
|
||||
}
|
||||
|
||||
public Integer getSchId() {
|
||||
return schId;
|
||||
}
|
||||
|
||||
public void setSchId(Integer schId) {
|
||||
this.schId = schId;
|
||||
}
|
||||
|
||||
public String getSchName() {
|
||||
return schName;
|
||||
}
|
||||
|
||||
public void setSchName(String schName) {
|
||||
this.schName = schName;
|
||||
}
|
||||
|
||||
public Integer getFirstTeacherId() {
|
||||
return firstTeacherId;
|
||||
}
|
||||
|
||||
public void setFirstTeacherId(Integer firstTeacherId) {
|
||||
this.firstTeacherId = firstTeacherId;
|
||||
}
|
||||
|
||||
public String getFirstTeacherName() {
|
||||
return firstTeacherName;
|
||||
}
|
||||
|
||||
public void setFirstTeacherName(String firstTeacherName) {
|
||||
this.firstTeacherName = firstTeacherName;
|
||||
}
|
||||
|
||||
public Integer getCampusId() {
|
||||
return campusId;
|
||||
}
|
||||
|
||||
public void setCampusId(Integer campusId) {
|
||||
this.campusId = campusId;
|
||||
}
|
||||
|
||||
public Integer getEnrollYear() {
|
||||
return enrollYear;
|
||||
}
|
||||
|
||||
public void setEnrollYear(Integer enrollYear) {
|
||||
this.enrollYear = enrollYear;
|
||||
}
|
||||
|
||||
public Boolean getGraduate() {
|
||||
return graduate;
|
||||
}
|
||||
|
||||
public void setGraduate(Boolean graduate) {
|
||||
this.graduate = graduate;
|
||||
}
|
||||
|
||||
public Integer getSort() {
|
||||
return sort;
|
||||
}
|
||||
|
||||
public void setSort(Integer sort) {
|
||||
this.sort = sort;
|
||||
}
|
||||
|
||||
/** 映射到本应用的年级(1/2/3);非高中学段返回 null */
|
||||
public Integer highGrade() {
|
||||
return gradeId == null ? null : HIGH_GRADE_MAP.get(gradeId);
|
||||
}
|
||||
|
||||
/** 展示名:年级名 + 班级名(如 高一1班);年级名缺失时退化为班级名 */
|
||||
public String displayName() {
|
||||
String n = name == null ? "" : name.trim();
|
||||
String g = gradeName == null ? "" : gradeName.trim();
|
||||
if (g.isEmpty()) {
|
||||
return n;
|
||||
}
|
||||
// 年级名已是完整班级名前缀(如平台侧直接叫"高一1班")时不重复拼接
|
||||
return n.startsWith(g) ? n : g + n;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package com.yunhai.acaclassroom.repository;
|
||||
|
||||
import com.yunhai.acaclassroom.entity.SchoolClass;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 班级仓储。单校班级量级为十~百级,整取后服务层过滤。
|
||||
*/
|
||||
public interface SchoolClassRepository extends JpaRepository<SchoolClass, Integer> {
|
||||
|
||||
List<SchoolClass> findBySchIdOrderByGradeAscIdAsc(Integer schId);
|
||||
|
||||
boolean existsBySchIdAndClassName(Integer schId, String className);
|
||||
}
|
||||
@@ -4,21 +4,18 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 班级档案服务(绑定班级页)。
|
||||
* 班级服务(绑定班级页数据源)。
|
||||
*
|
||||
* <p>班级数据实时取自班级管理(bschclass)开放接口,不在本应用落库;
|
||||
* 教室绑定关系存 {@code aca_classroom.class_id}(平台分组体系 classId)。</p>
|
||||
*/
|
||||
public interface IClassService {
|
||||
|
||||
/**
|
||||
* 班级列表(全校,可按年级过滤)。
|
||||
* 班级列表(全校高中学段,可按年级过滤)。
|
||||
*
|
||||
* @param grade 1=高一 2=高二 3=高三(可空=全部)
|
||||
* @return 行含 serID/className/grade/studentCount/headTeacher/classroomId(未绑定为 null)
|
||||
* @return 行含 serID(=平台 classId)/className/grade/studentCount(可空)/headTeacher(可空)/classroomId(未绑定为 null)
|
||||
*/
|
||||
List<Map<String, Object>> list(Integer grade);
|
||||
|
||||
/** 新增班级,返回 id。 */
|
||||
Integer add(String className, Integer grade, Integer studentCount, String headTeacher);
|
||||
|
||||
/** 删除班级(已被教室绑定时拒绝)。 */
|
||||
void delete(Integer id);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
package com.yunhai.acaclassroom.service.impl;
|
||||
|
||||
import com.yunhai.acaclassroom.model.PlatformSchoolClass;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.client.RestClientException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 班级管理(bschclass)开放接口客户端,绑定班级页班级数据的唯一来源。
|
||||
*
|
||||
* <p>接口口径与 AcaClassAssign 一致(配置 {@code yh.open.bschclass}):</p>
|
||||
* <ul>
|
||||
* <li>班级列表:GET {@code /oapi/sch/getSchClassList?schId=&includeGraduate=false}(不含毕业班);</li>
|
||||
* <li>学生树:GET {@code /oapi/SchGroup/GetStuTree?schId=&toUser=true},
|
||||
* 学校→学段→年级→班级→学生五层,班级节点 id 即班级列表的 classId,
|
||||
* 其下 type=2 子节点为学生,用于派生各班学生人数。</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>两个接口均做 5 分钟内存缓存(TTL,按 schId);拉取失败回退上一份缓存,
|
||||
* 无缓存时降级为空(列表)/空映射(人数),不阻断页面——与同族应用降级口径一致。</p>
|
||||
*/
|
||||
@Component
|
||||
public class BschClassClient {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(BschClassClient.class);
|
||||
|
||||
/** 缓存有效期 ms */
|
||||
private static final long CACHE_TTL_MS = 5 * 60 * 1000L;
|
||||
|
||||
/** 学生树中学生节点 type */
|
||||
private static final Integer STU_NODE_TYPE = 2;
|
||||
|
||||
/** 班级管理服务基址 */
|
||||
@Value("${yh.open.bschclass:}")
|
||||
private String bschClassHost;
|
||||
|
||||
private final RestTemplate restTemplate;
|
||||
|
||||
/** 班级列表缓存:schId -> {time, list} */
|
||||
private final ConcurrentHashMap<Integer, ClassListCacheEntry> classListCache = new ConcurrentHashMap<>();
|
||||
|
||||
/** 学生人数缓存:schId -> {time, classId -> 人数} */
|
||||
private final ConcurrentHashMap<Integer, StuCountCacheEntry> stuCountCache = new ConcurrentHashMap<>();
|
||||
|
||||
private static class ClassListCacheEntry {
|
||||
final long time;
|
||||
final List<PlatformSchoolClass> list;
|
||||
|
||||
ClassListCacheEntry(long time, List<PlatformSchoolClass> list) {
|
||||
this.time = time;
|
||||
this.list = list;
|
||||
}
|
||||
}
|
||||
|
||||
private static class StuCountCacheEntry {
|
||||
final long time;
|
||||
final Map<Integer, Integer> counts;
|
||||
|
||||
StuCountCacheEntry(long time, Map<Integer, Integer> counts) {
|
||||
this.time = time;
|
||||
this.counts = counts;
|
||||
}
|
||||
}
|
||||
|
||||
public BschClassClient() {
|
||||
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
|
||||
factory.setConnectTimeout(5000);
|
||||
factory.setReadTimeout(10000);
|
||||
this.restTemplate = new RestTemplate(factory);
|
||||
}
|
||||
|
||||
/** 学校班级列表(不含毕业班,5 分钟缓存;不可达回退旧缓存,无缓存降级空列表)。 */
|
||||
public List<PlatformSchoolClass> classList(int schId) {
|
||||
long now = System.currentTimeMillis();
|
||||
ClassListCacheEntry cached = classListCache.get(schId);
|
||||
if (cached != null && now - cached.time < CACHE_TTL_MS) {
|
||||
return cached.list;
|
||||
}
|
||||
synchronized (this) {
|
||||
cached = classListCache.get(schId);
|
||||
if (cached != null && now - cached.time < CACHE_TTL_MS) {
|
||||
return cached.list;
|
||||
}
|
||||
List<PlatformSchoolClass> list = fetchClassList(schId);
|
||||
if (list == null) {
|
||||
return cached != null ? cached.list : Collections.emptyList();
|
||||
}
|
||||
classListCache.put(schId, new ClassListCacheEntry(now, list));
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
/** 班级 id → 学生人数(学生树派生,5 分钟缓存;树不可达回退旧缓存,无缓存降级空映射)。 */
|
||||
public Map<Integer, Integer> studentCounts(int schId) {
|
||||
long now = System.currentTimeMillis();
|
||||
StuCountCacheEntry cached = stuCountCache.get(schId);
|
||||
if (cached != null && now - cached.time < CACHE_TTL_MS) {
|
||||
return cached.counts;
|
||||
}
|
||||
synchronized (this) {
|
||||
cached = stuCountCache.get(schId);
|
||||
if (cached != null && now - cached.time < CACHE_TTL_MS) {
|
||||
return cached.counts;
|
||||
}
|
||||
Map<Integer, Integer> counts = fetchStudentCounts(schId);
|
||||
if (counts == null) {
|
||||
return cached != null ? cached.counts : Collections.emptyMap();
|
||||
}
|
||||
stuCountCache.put(schId, new StuCountCacheEntry(now, counts));
|
||||
return counts;
|
||||
}
|
||||
}
|
||||
|
||||
/** 按班级 id 找单个班级(走列表缓存);不存在返回 null。 */
|
||||
public PlatformSchoolClass find(int schId, Integer classId) {
|
||||
if (classId == null) {
|
||||
return null;
|
||||
}
|
||||
return classList(schId).stream()
|
||||
.filter(c -> classId.equals(c.getClassId()))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private List<PlatformSchoolClass> fetchClassList(int schId) {
|
||||
if (!StringUtils.hasText(bschClassHost)) {
|
||||
log.warn("[fetchClassList] 班级管理接口未配置(yh.open.bschclass)");
|
||||
return Collections.emptyList();
|
||||
}
|
||||
String url = bschClassHost + "/oapi/sch/getSchClassList?schId=" + schId + "&includeGraduate=false";
|
||||
try {
|
||||
ResponseEntity<List<PlatformSchoolClass>> resp = restTemplate.exchange(
|
||||
url, HttpMethod.GET, null,
|
||||
new ParameterizedTypeReference<List<PlatformSchoolClass>>() {
|
||||
});
|
||||
List<PlatformSchoolClass> body = resp.getBody();
|
||||
log.info("[fetchClassList] schId={}, 班级 {} 个", schId, body == null ? 0 : body.size());
|
||||
return body != null ? body : Collections.emptyList();
|
||||
} catch (RestClientException e) {
|
||||
log.error("[fetchClassList] 请求班级列表失败, schId={}, url={}", schId, url, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** 拉学生树并按班级节点计数;不可达返回 null(区分"树为空")。 */
|
||||
private Map<Integer, Integer> fetchStudentCounts(int schId) {
|
||||
if (!StringUtils.hasText(bschClassHost)) {
|
||||
log.warn("[fetchStudentCounts] 班级管理接口未配置(yh.open.bschclass)");
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
String url = bschClassHost + "/oapi/SchGroup/GetStuTree?schId=" + schId + "&toUser=true";
|
||||
try {
|
||||
ResponseEntity<List<Map<String, Object>>> resp = restTemplate.exchange(
|
||||
url, HttpMethod.GET, null,
|
||||
new ParameterizedTypeReference<List<Map<String, Object>>>() {
|
||||
});
|
||||
List<Map<String, Object>> body = resp.getBody();
|
||||
Map<Integer, Integer> counts = new LinkedHashMap<>();
|
||||
if (body != null) {
|
||||
// 固定层级 学校→学段→年级→班级→学生(与 AcaClassAssign buildIndex 同一口径):
|
||||
// 第 4 层班级节点 id 即班级列表 classId,其子节点 type=2 的为学生
|
||||
for (Map<String, Object> school : orEmpty(body)) {
|
||||
for (Map<String, Object> system : childrenOf(school)) {
|
||||
for (Map<String, Object> grade : childrenOf(system)) {
|
||||
for (Map<String, Object> clazz : childrenOf(grade)) {
|
||||
Integer classId = asInt(clazz.get("id"));
|
||||
if (classId != null) {
|
||||
counts.put(classId, (int) childrenOf(clazz).stream()
|
||||
.filter(stu -> stu != null && STU_NODE_TYPE.equals(asInt(stu.get("type"))))
|
||||
.count());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
log.info("[fetchStudentCounts] schId={}, 班级人数 {} 项", schId, counts.size());
|
||||
return counts;
|
||||
} catch (RestClientException e) {
|
||||
log.error("[fetchStudentCounts] 请求学生树失败, schId={}, url={}", schId, url, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<Map<String, Object>> childrenOf(Map<String, Object> node) {
|
||||
if (node == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
Object children = node.get("children");
|
||||
return children instanceof List ? (List<Map<String, Object>>) children : Collections.emptyList();
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> orEmpty(List<Map<String, Object>> list) {
|
||||
return list != null ? list : Collections.emptyList();
|
||||
}
|
||||
|
||||
private Integer asInt(Object v) {
|
||||
if (v == null) {
|
||||
return null;
|
||||
}
|
||||
if (v instanceof Number) {
|
||||
return ((Number) v).intValue();
|
||||
}
|
||||
try {
|
||||
return Integer.valueOf(String.valueOf(v));
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,28 @@
|
||||
package com.yunhai.acaclassroom.service.impl;
|
||||
|
||||
import com.yunhai.acaclassroom.entity.Classroom;
|
||||
import com.yunhai.acaclassroom.entity.SchoolClass;
|
||||
import com.yunhai.acaclassroom.model.PlatformSchoolClass;
|
||||
import com.yunhai.acaclassroom.repository.ClassroomRepository;
|
||||
import com.yunhai.acaclassroom.repository.SchoolClassRepository;
|
||||
import com.yunhai.acaclassroom.service.IClassService;
|
||||
import com.yunhai.acaclassroom.utils.UserContext;
|
||||
import com.yunhai.platform.apistd.BizException;
|
||||
import com.yunhai.platform.utils.exception.ErrorCode;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 班级档案服务实现。班级数据量小(单校十~百级),整取后内存过滤。
|
||||
* 班级服务实现(绑定班级页数据源)。
|
||||
*
|
||||
* <p>班级档案不在本应用落库:班级数据实时取自班级管理(bschclass)开放接口
|
||||
* {@link BschClassClient}(5 分钟缓存、失败降级),绑定关系仍存本应用
|
||||
* {@code aca_classroom.class_id}(存平台分组体系 classId)。此前本地档案表
|
||||
* {@code aca_class} 及 class/Add·Delete 接口已随数据源切换移除。</p>
|
||||
*/
|
||||
@Service
|
||||
public class ClassService implements IClassService {
|
||||
|
||||
@Autowired
|
||||
private SchoolClassRepository classRepository;
|
||||
private BschClassClient bschClassClient;
|
||||
|
||||
@Autowired
|
||||
private ClassroomRepository classroomRepository;
|
||||
@@ -33,72 +33,35 @@ public class ClassService implements IClassService {
|
||||
if (schId == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<SchoolClass> classes = classRepository.findBySchIdOrderByGradeAscIdAsc(schId);
|
||||
// 仅保留高中学段(gradeId 10/11/12 → 1/2/3),其余学段不进绑定页
|
||||
List<PlatformSchoolClass> classes = bschClassClient.classList(schId).stream()
|
||||
.filter(c -> c.highGrade() != null && c.getClassId() != null)
|
||||
.sorted(Comparator
|
||||
.comparingInt((PlatformSchoolClass c) -> c.highGrade())
|
||||
.thenComparing(c -> c.getSort() == null ? Integer.MAX_VALUE : c.getSort())
|
||||
.thenComparing(c -> c.getId() == null ? Integer.MAX_VALUE : c.getId()))
|
||||
.collect(Collectors.toList());
|
||||
if (grade != null) {
|
||||
classes = classes.stream().filter(c -> grade.equals(c.getGrade())).collect(Collectors.toList());
|
||||
classes = classes.stream().filter(c -> grade.equals(c.highGrade())).collect(Collectors.toList());
|
||||
}
|
||||
// 班级 → 绑定的教室 id(绑定状态下拉过滤/行回显用)
|
||||
List<Integer> ids = classes.stream().map(SchoolClass::getId).collect(Collectors.toList());
|
||||
List<Integer> ids = classes.stream().map(PlatformSchoolClass::getClassId).collect(Collectors.toList());
|
||||
Map<Integer, Integer> boundRooms = ids.isEmpty()
|
||||
? Collections.emptyMap()
|
||||
: classroomRepository.findBySchIdAndClassIdIn(schId, ids).stream()
|
||||
.collect(Collectors.toMap(Classroom::getClassId, Classroom::getId, (a, b) -> a));
|
||||
.collect(Collectors.toMap(com.yunhai.acaclassroom.entity.Classroom::getClassId,
|
||||
com.yunhai.acaclassroom.entity.Classroom::getId, (a, b) -> a));
|
||||
// 学生人数来自学生树(树不可达时为空映射 → 前端隐藏该段)
|
||||
Map<Integer, Integer> stuCounts = bschClassClient.studentCounts(schId);
|
||||
return classes.stream().map(c -> {
|
||||
Map<String, Object> vo = new LinkedHashMap<>();
|
||||
vo.put("serID", c.getId());
|
||||
vo.put("className", c.getClassName());
|
||||
vo.put("grade", c.getGrade());
|
||||
vo.put("studentCount", c.getStudentCount());
|
||||
vo.put("headTeacher", c.getHeadTeacher());
|
||||
vo.put("classroomId", boundRooms.get(c.getId()));
|
||||
vo.put("serID", c.getClassId());
|
||||
vo.put("className", c.displayName());
|
||||
vo.put("grade", c.highGrade());
|
||||
vo.put("studentCount", stuCounts.get(c.getClassId()));
|
||||
vo.put("headTeacher", c.getFirstTeacherName());
|
||||
vo.put("classroomId", boundRooms.get(c.getClassId()));
|
||||
return vo;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Integer add(String className, Integer grade, Integer studentCount, String headTeacher) {
|
||||
Integer schId = UserContext.getSchId();
|
||||
if (schId == null) {
|
||||
throw new BizException(ErrorCode.INTERNAL_SERVER_ERROR, "未能获取当前学校信息,请重新登录");
|
||||
}
|
||||
String name = className != null ? className.trim() : "";
|
||||
if (name.isEmpty() || name.length() > 50) {
|
||||
throw new BizException(ErrorCode.PARAM_ERROR, "请输入 50 字以内的班级名称");
|
||||
}
|
||||
if (grade == null || grade < SchoolClass.GRADE_1 || grade > SchoolClass.GRADE_3) {
|
||||
throw new BizException(ErrorCode.PARAM_ERROR, "年级只支持 1=高一 2=高二 3=高三");
|
||||
}
|
||||
if (classRepository.existsBySchIdAndClassName(schId, name)) {
|
||||
throw new BizException(ErrorCode.PARAM_ERROR, "班级名称已存在");
|
||||
}
|
||||
int count = studentCount == null ? 0 : studentCount;
|
||||
if (count < 0 || count > 10000) {
|
||||
throw new BizException(ErrorCode.PARAM_ERROR, "学生人数不正确");
|
||||
}
|
||||
SchoolClass c = new SchoolClass();
|
||||
c.setClassName(name);
|
||||
c.setGrade(grade);
|
||||
c.setStudentCount(count);
|
||||
c.setHeadTeacher(headTeacher != null && !headTeacher.trim().isEmpty() ? headTeacher.trim() : null);
|
||||
c.setSchId(schId);
|
||||
return classRepository.save(c).getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void delete(Integer id) {
|
||||
Integer schId = UserContext.getSchId();
|
||||
if (schId == null) {
|
||||
throw new BizException(ErrorCode.INTERNAL_SERVER_ERROR, "未能获取当前学校信息,请重新登录");
|
||||
}
|
||||
SchoolClass c = classRepository.findById(id).orElse(null);
|
||||
if (c == null || !schId.equals(c.getSchId())) {
|
||||
throw new BizException(ErrorCode.PARAM_ERROR, "班级不存在或无权操作");
|
||||
}
|
||||
if (classroomRepository.existsBySchIdAndClassId(schId, id)) {
|
||||
throw new BizException(ErrorCode.PARAM_ERROR, "该班级已绑定教室,请先解绑再删除");
|
||||
}
|
||||
classRepository.delete(c);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.yunhai.acaclassroom.entity.ClassroomType;
|
||||
import com.yunhai.acaclassroom.model.ClassroomBatchAddRequest;
|
||||
import com.yunhai.acaclassroom.model.ClassroomBatchEditRequest;
|
||||
import com.yunhai.acaclassroom.model.ClassroomEditRequest;
|
||||
import com.yunhai.acaclassroom.model.PlatformSchoolClass;
|
||||
import com.yunhai.acaclassroom.repository.ClassroomRepository;
|
||||
import com.yunhai.acaclassroom.repository.ClassroomTypeRepository;
|
||||
import com.yunhai.acaclassroom.repository.EquipmentRepository;import com.yunhai.acaclassroom.service.IClassroomService;
|
||||
@@ -49,7 +50,7 @@ public class ClassroomService implements IClassroomService {
|
||||
private com.yunhai.acaclassroom.repository.BuildingRepository buildingRepository;
|
||||
|
||||
@Autowired
|
||||
private com.yunhai.acaclassroom.repository.SchoolClassRepository classRepository;
|
||||
private BschClassClient bschClassClient;
|
||||
|
||||
@Autowired
|
||||
private BuildingService buildingService;
|
||||
@@ -93,10 +94,11 @@ public class ClassroomService implements IClassroomService {
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
// 班级映射:绑定状态过滤 / 年级 Tab 过滤 / 行内班级信息展示共用
|
||||
Map<Integer, com.yunhai.acaclassroom.entity.SchoolClass> classMap = classRepository
|
||||
.findBySchIdOrderByGradeAscIdAsc(schId).stream()
|
||||
.collect(Collectors.toMap(com.yunhai.acaclassroom.entity.SchoolClass::getId, c -> c, (a, b) -> a));
|
||||
// 班级映射(平台班级,键=分组体系 classId):绑定状态过滤 / 年级 Tab 过滤 / 行内班级信息展示共用
|
||||
Map<Integer, PlatformSchoolClass> classMap = bschClassClient.classList(schId).stream()
|
||||
.filter(c -> c.getClassId() != null)
|
||||
.collect(Collectors.toMap(PlatformSchoolClass::getClassId, c -> c, (a, b) -> a));
|
||||
Map<Integer, Integer> stuCounts = bschClassClient.studentCounts(schId);
|
||||
if ("bound".equals(bindStatus)) {
|
||||
rooms = rooms.stream().filter(r -> r.getClassId() != null).collect(Collectors.toList());
|
||||
} else if ("unbound".equals(bindStatus)) {
|
||||
@@ -108,8 +110,8 @@ public class ClassroomService implements IClassroomService {
|
||||
if (r.getClassId() == null) {
|
||||
return true;
|
||||
}
|
||||
com.yunhai.acaclassroom.entity.SchoolClass c = classMap.get(r.getClassId());
|
||||
return c != null && grade.equals(c.getGrade());
|
||||
PlatformSchoolClass c = classMap.get(r.getClassId());
|
||||
return c != null && grade.equals(c.highGrade());
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@@ -163,11 +165,11 @@ public class ClassroomService implements IClassroomService {
|
||||
Map<String, Object> vo = toVo(r, typeNames);
|
||||
vo.put("campusName", campusNames.getOrDefault(r.getCampusId(), ""));
|
||||
vo.put("buildingName", buildingNames.getOrDefault(r.getBuildingId(), ""));
|
||||
com.yunhai.acaclassroom.entity.SchoolClass bound = r.getClassId() != null ? classMap.get(r.getClassId()) : null;
|
||||
PlatformSchoolClass bound = r.getClassId() != null ? classMap.get(r.getClassId()) : null;
|
||||
vo.put("classId", r.getClassId());
|
||||
vo.put("className", bound != null ? bound.getClassName() : null);
|
||||
vo.put("studentCount", bound != null ? bound.getStudentCount() : null);
|
||||
vo.put("headTeacher", bound != null ? bound.getHeadTeacher() : null);
|
||||
vo.put("className", bound != null ? bound.displayName() : null);
|
||||
vo.put("studentCount", bound != null ? stuCounts.get(r.getClassId()) : null);
|
||||
vo.put("headTeacher", bound != null ? bound.getFirstTeacherName() : null);
|
||||
Map<Integer, Integer> eqQty = roomEquipmentQty.getOrDefault(r.getId(), Collections.emptyMap());
|
||||
vo.put("equipmentIds", new ArrayList<>(eqQty.keySet()));
|
||||
vo.put("equipmentQtys", eqQty);
|
||||
@@ -473,8 +475,9 @@ public class ClassroomService implements IClassroomService {
|
||||
throw new BizException(ErrorCode.PARAM_ERROR, "教室ID与班级ID不得为空");
|
||||
}
|
||||
Classroom r = loadOwned(serID);
|
||||
com.yunhai.acaclassroom.entity.SchoolClass c = classRepository.findById(classId).orElse(null);
|
||||
if (c == null || !r.getSchId().equals(c.getSchId())) {
|
||||
// 班级以班级管理平台数据为准:须存在于本校班级列表(班级不在本应用落库)
|
||||
PlatformSchoolClass c = bschClassClient.find(r.getSchId(), classId);
|
||||
if (c == null) {
|
||||
throw new BizException(ErrorCode.PARAM_ERROR, "班级不存在或无权操作");
|
||||
}
|
||||
// 一个班级同时只能绑一间教室
|
||||
@@ -488,7 +491,7 @@ public class ClassroomService implements IClassroomService {
|
||||
classroomRepository.save(r);
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("classId", classId);
|
||||
result.put("className", c.getClassName());
|
||||
result.put("className", c.displayName());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user