From 40bc49a46e6eb15ef14058f6f5803c24ffd95cf0 Mon Sep 17 00:00:00 2001 From: wanghu Date: Sun, 20 Sep 2026 14:13:42 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=BF=E8=88=8D=E9=80=BB=E8=BE=91=E4=BF=AE?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/api/DormController.java | 35 ++++- .../dormnotice/service/IDormService.java | 13 +- .../dormnotice/service/impl/DormService.java | 137 +++++++++++++----- 3 files changed, 146 insertions(+), 39 deletions(-) diff --git a/src/main/java/com/yunhai/dormnotice/controller/api/DormController.java b/src/main/java/com/yunhai/dormnotice/controller/api/DormController.java index ed133f0..89a5fc4 100644 --- a/src/main/java/com/yunhai/dormnotice/controller/api/DormController.java +++ b/src/main/java/com/yunhai/dormnotice/controller/api/DormController.java @@ -106,14 +106,41 @@ public class DormController { dormService.setMonitor(roomId, studentId); } - /** 从 bschclass 学籍导入全校学生到待安排池(幂等),返回新增人数。 */ - @PostMapping("ImportStudents") - public Map importStudents() { + /** 可添加学生名单:bschclass 在册学生,排除已保存到 dorm_student 的(供前端勾选)。 */ + @GetMapping("AvailableStudents") + public List> availableStudents(@RequestParam(required = false) String keyword) { + return dormService.availableStudents(keyword); + } + + /** 保存前端勾选的学生到待安排池(roomId=0),返回新增人数。 */ + @PostMapping("SaveStudents") + public Map saveStudents(@RequestParam String studentIds) { Map r = new java.util.LinkedHashMap<>(); - r.put("added", dormService.importStudentsFromBschclass()); + r.put("added", dormService.saveStudents(parseIds(studentIds))); return r; } + /** 手动删除已保存的学生(在住者重排该房床位),返回删除人数。 */ + @PostMapping("DeleteStudents") + public Map deleteStudents(@RequestParam String studentIds) { + Map r = new java.util.LinkedHashMap<>(); + r.put("removed", dormService.deleteStudents(parseIds(studentIds))); + return r; + } + + private static List parseIds(String csv) { + List ids = new java.util.ArrayList<>(); + if (csv != null) { + for (String s : csv.split(",")) { + String t = s.trim(); + if (!t.isEmpty()) { + ids.add(Integer.parseInt(t)); + } + } + } + return ids; + } + private static Map toObj(Map src) { return src.entrySet().stream().collect(java.util.stream.Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); } diff --git a/src/main/java/com/yunhai/dormnotice/service/IDormService.java b/src/main/java/com/yunhai/dormnotice/service/IDormService.java index 8e7c652..0389aca 100644 --- a/src/main/java/com/yunhai/dormnotice/service/IDormService.java +++ b/src/main/java/com/yunhai/dormnotice/service/IDormService.java @@ -41,6 +41,15 @@ public interface IDormService { /** 楼栋→楼层→房间树(ztree,供发布选房间;对应 base/GetDormTree)。 */ List> dormTree(); - /** 从 bschclass 学籍导入全校学生到「待安排池」(roomId=0),幂等;返回新增人数。 */ - int importStudentsFromBschclass(); + /** + * 可添加学生名单:从 bschclass 学籍拉取本校在册学生,排除已保存到 dorm_student 的,供前端勾选。 + * 返回项:{studentId, studentName, className}。 + */ + List> availableStudents(String keyword); + + /** 保存前端勾选的学生到 dorm_student(roomId=0 待安排池),已存在的跳过;返回新增人数。 */ + int saveStudents(List studentIds); + + /** 手动删除 dorm_student 中的学生(在住者删除后重排该房床位);返回删除人数。 */ + int deleteStudents(List studentIds); } diff --git a/src/main/java/com/yunhai/dormnotice/service/impl/DormService.java b/src/main/java/com/yunhai/dormnotice/service/impl/DormService.java index 12847d2..f72276a 100644 --- a/src/main/java/com/yunhai/dormnotice/service/impl/DormService.java +++ b/src/main/java/com/yunhai/dormnotice/service/impl/DormService.java @@ -108,19 +108,8 @@ public class DormService implements IDormService { @Override public List> pendingStudents(String keyword, String gender) { - // 待安排池:dorm_student 中 roomId=0/null。池空时惰性从 bschclass 学籍导入一次, - // 让「安排入住」有数据源;导入失败(平台不可达等)不影响页面,返回空池。 + // 待安排池:dorm_student 中 roomId=0(由「添加学生」手动保存进来的在册学生) List raw = studentRepository.findByRoomIdOrderByIdAsc(0); - if (raw.isEmpty()) { - try { - int n = importStudentsFromBschclass(); - if (n > 0) { - raw = studentRepository.findByRoomIdOrderByIdAsc(0); - } - } catch (Exception e) { - log.warn("[pendingStudents] 从 bschclass 导入学生失败,返回空池: {}", e.getMessage()); - } - } List pool = raw.stream() .filter(s -> !StringUtils.hasText(gender) || gender.equals(s.getGender())) .filter(s -> !StringUtils.hasText(keyword) @@ -378,45 +367,98 @@ public class DormService implements IDormService { } } - // ============================ bschclass 学籍导入 ============================ + // ============================ bschclass 学籍(前端选择保存) ============================ - @Override - @Transactional - public int importStudentsFromBschclass() { + private static final class Roster { + final List students; + final Map classNames; + Roster(List students, Map classNames) { + this.students = students; + this.classNames = classNames; + } + } + + /** 拉取本校在册学生 + 班级名映射;无学校上下文/失败/空返回 null(调用方按空处理)。 */ + private Roster fetchRoster() { Integer schId = UserContext.getSchId(); if (schId == null) { - throw new BizException(ErrorCode.PARAM_ERROR, "未获取到学校上下文,无法导入学生"); + return null; } SchInfoDto info = classService.getSchFirst(schId); if (info == null || !StringUtils.hasText(info.getSchSerId())) { - log.warn("[importStudents] schId={} 未取到 bschclass SchSerId", schId); - return 0; + log.warn("[dorm] schId={} 未取到 bschclass SchSerId", schId); + return null; } - String schSerId = info.getSchSerId(); - List stus = classService.getSchStuList(schSerId); + List stus = classService.getSchStuList(info.getSchSerId()); if (stus == null || stus.isEmpty()) { - return 0; + return null; } Map classNames = new HashMap<>(); try { - for (SchClassDto c : classService.getSchClassList(schSerId)) { + for (SchClassDto c : classService.getSchClassList(info.getSchSerId())) { classNames.put(c.getClassId(), c.getClassName()); } } catch (Exception e) { - log.warn("[importStudents] 取班级名单失败(班级名留空): {}", e.getMessage()); + log.warn("[dorm] 取班级名单失败(班级名留空): {}", e.getMessage()); } - int added = 0; - for (SchStudentDto s : stus) { - if (s.getStuId() == null) { + return new Roster(stus, classNames); + } + + @Override + public List> availableStudents(String keyword) { + List> out = new ArrayList<>(); + Roster roster = fetchRoster(); + if (roster == null) { + return out; + } + Set savedIds = studentRepository.findAll().stream() + .map(DormStudent::getStudentId).filter(Objects::nonNull).collect(Collectors.toSet()); + String kw = keyword == null ? "" : keyword.trim(); + for (SchStudentDto s : roster.students) { + if (s.getStuId() == null || savedIds.contains(s.getStuId())) { + continue; // 排除已保存到 dorm_student 的 + } + if (!kw.isEmpty() && !String.valueOf(s.getStuName()).contains(kw)) { continue; } - if (studentRepository.findByStudentId(s.getStuId()).isPresent()) { - continue; // 已存在(在住或已在池)跳过,幂等 + Map m = new LinkedHashMap<>(); + m.put("studentId", s.getStuId()); + m.put("studentName", s.getStuName()); + m.put("className", roster.classNames.get(s.getClassId())); + out.add(m); + } + return out; + } + + @Override + @Transactional + public int saveStudents(List studentIds) { + if (studentIds == null || studentIds.isEmpty()) { + return 0; + } + Integer schId = UserContext.getSchId(); + // 用最新在册名单补全姓名/班级(拿不到也不阻断,按 id 落库) + Map rosterById = new HashMap<>(); + Map classNames = new HashMap<>(); + Roster roster = fetchRoster(); + if (roster != null) { + for (SchStudentDto s : roster.students) { + if (s.getStuId() != null) { + rosterById.put(s.getStuId(), s); + } } + classNames = roster.classNames; + } + int added = 0; + for (Integer id : studentIds) { + if (id == null || studentRepository.findByStudentId(id).isPresent()) { + continue; // 已存在跳过,幂等 + } + SchStudentDto s = rosterById.get(id); DormStudent d = new DormStudent(); - d.setStudentId(s.getStuId()); - d.setStudentName(s.getStuName()); - d.setClassName(classNames.get(s.getClassId())); + d.setStudentId(id); + d.setStudentName(s != null ? s.getStuName() : null); + d.setClassName(s != null ? classNames.get(s.getClassId()) : null); d.setRoomId(0); d.setIsMonitor(false); d.setSourceDesc("学籍在册未分配床位"); @@ -424,10 +466,39 @@ public class DormService implements IDormService { studentRepository.save(d); added++; } - log.info("[importStudents] schId={} schSerId={} 学籍 {} 人,新导入待安排 {} 人", schId, schSerId, stus.size(), added); + log.info("[saveStudents] schId={} 勾选 {} 人,新增保存 {} 人", schId, studentIds.size(), added); return added; } + @Override + @Transactional + public int deleteStudents(List studentIds) { + if (studentIds == null || studentIds.isEmpty()) { + return 0; + } + int removed = 0; + Set roomsToRenumber = new HashSet<>(); + for (Integer id : studentIds) { + if (id == null) { + continue; + } + Optional opt = studentRepository.findByStudentId(id); + if (opt.isPresent()) { + Integer rid = opt.get().getRoomId(); + studentRepository.delete(opt.get()); + if (rid != null && rid > 0) { + roomsToRenumber.add(rid); + } + removed++; + } + } + for (Integer rid : roomsToRenumber) { + renumberBeds(rid); + } + log.info("[deleteStudents] 手动删除 {} 人", removed); + return removed; + } + // ============================ 辅助 ============================ private List buildingsOfSch() {