宿舍逻辑修改
build / build-push (push) Successful in 54s

This commit is contained in:
wanghu
2026-09-20 14:13:42 +08:00
parent c70d3e68c2
commit 40bc49a46e
3 changed files with 146 additions and 39 deletions
@@ -106,14 +106,41 @@ public class DormController {
dormService.setMonitor(roomId, studentId); dormService.setMonitor(roomId, studentId);
} }
/** 从 bschclass 学籍导入全校学生到待安排池(幂等),返回新增人数。 */ /** 可添加学生名单:bschclass 在册学生,排除已保存到 dorm_student 的(供前端勾选)。 */
@PostMapping("ImportStudents") @GetMapping("AvailableStudents")
public Map<String, Object> importStudents() { public List<Map<String, Object>> availableStudents(@RequestParam(required = false) String keyword) {
return dormService.availableStudents(keyword);
}
/** 保存前端勾选的学生到待安排池(roomId=0),返回新增人数。 */
@PostMapping("SaveStudents")
public Map<String, Object> saveStudents(@RequestParam String studentIds) {
Map<String, Object> r = new java.util.LinkedHashMap<>(); Map<String, Object> r = new java.util.LinkedHashMap<>();
r.put("added", dormService.importStudentsFromBschclass()); r.put("added", dormService.saveStudents(parseIds(studentIds)));
return r; return r;
} }
/** 手动删除已保存的学生(在住者重排该房床位),返回删除人数。 */
@PostMapping("DeleteStudents")
public Map<String, Object> deleteStudents(@RequestParam String studentIds) {
Map<String, Object> r = new java.util.LinkedHashMap<>();
r.put("removed", dormService.deleteStudents(parseIds(studentIds)));
return r;
}
private static List<Integer> parseIds(String csv) {
List<Integer> 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<String, Object> toObj(Map<String, String> src) { private static Map<String, Object> toObj(Map<String, String> src) {
return src.entrySet().stream().collect(java.util.stream.Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); return src.entrySet().stream().collect(java.util.stream.Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
} }
@@ -41,6 +41,15 @@ public interface IDormService {
/** 楼栋→楼层→房间树(ztree,供发布选房间;对应 base/GetDormTree)。 */ /** 楼栋→楼层→房间树(ztree,供发布选房间;对应 base/GetDormTree)。 */
List<Map<String, Object>> dormTree(); List<Map<String, Object>> dormTree();
/** 从 bschclass 学籍导入全校学生到「待安排池」(roomId=0),幂等;返回新增人数。 */ /**
int importStudentsFromBschclass(); * 可添加学生名单:从 bschclass 学籍拉取本校在册学生,排除已保存到 dorm_student 的,供前端勾选。
* 返回项:{studentId, studentName, className}。
*/
List<Map<String, Object>> availableStudents(String keyword);
/** 保存前端勾选的学生到 dorm_student(roomId=0 待安排池),已存在的跳过;返回新增人数。 */
int saveStudents(List<Integer> studentIds);
/** 手动删除 dorm_student 中的学生(在住者删除后重排该房床位);返回删除人数。 */
int deleteStudents(List<Integer> studentIds);
} }
@@ -108,19 +108,8 @@ public class DormService implements IDormService {
@Override @Override
public List<Map<String, Object>> pendingStudents(String keyword, String gender) { public List<Map<String, Object>> pendingStudents(String keyword, String gender) {
// 待安排池:dorm_student 中 roomId=0/null。池空时惰性从 bschclass 学籍导入一次, // 待安排池:dorm_student 中 roomId=0(由「添加学生」手动保存进来的在册学生)
// 让「安排入住」有数据源;导入失败(平台不可达等)不影响页面,返回空池。
List<DormStudent> raw = studentRepository.findByRoomIdOrderByIdAsc(0); List<DormStudent> 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<DormStudent> pool = raw.stream() List<DormStudent> pool = raw.stream()
.filter(s -> !StringUtils.hasText(gender) || gender.equals(s.getGender())) .filter(s -> !StringUtils.hasText(gender) || gender.equals(s.getGender()))
.filter(s -> !StringUtils.hasText(keyword) .filter(s -> !StringUtils.hasText(keyword)
@@ -378,45 +367,98 @@ public class DormService implements IDormService {
} }
} }
// ============================ bschclass 学籍导入 ============================ // ============================ bschclass 学籍(前端选择保存) ============================
@Override private static final class Roster {
@Transactional final List<SchStudentDto> students;
public int importStudentsFromBschclass() { final Map<Integer, String> classNames;
Roster(List<SchStudentDto> students, Map<Integer, String> classNames) {
this.students = students;
this.classNames = classNames;
}
}
/** 拉取本校在册学生 + 班级名映射;无学校上下文/失败/空返回 null(调用方按空处理)。 */
private Roster fetchRoster() {
Integer schId = UserContext.getSchId(); Integer schId = UserContext.getSchId();
if (schId == null) { if (schId == null) {
throw new BizException(ErrorCode.PARAM_ERROR, "未获取到学校上下文,无法导入学生"); return null;
} }
SchInfoDto info = classService.getSchFirst(schId); SchInfoDto info = classService.getSchFirst(schId);
if (info == null || !StringUtils.hasText(info.getSchSerId())) { if (info == null || !StringUtils.hasText(info.getSchSerId())) {
log.warn("[importStudents] schId={} 未取到 bschclass SchSerId", schId); log.warn("[dorm] schId={} 未取到 bschclass SchSerId", schId);
return 0; return null;
} }
String schSerId = info.getSchSerId(); List<SchStudentDto> stus = classService.getSchStuList(info.getSchSerId());
List<SchStudentDto> stus = classService.getSchStuList(schSerId);
if (stus == null || stus.isEmpty()) { if (stus == null || stus.isEmpty()) {
return 0; return null;
} }
Map<Integer, String> classNames = new HashMap<>(); Map<Integer, String> classNames = new HashMap<>();
try { try {
for (SchClassDto c : classService.getSchClassList(schSerId)) { for (SchClassDto c : classService.getSchClassList(info.getSchSerId())) {
classNames.put(c.getClassId(), c.getClassName()); classNames.put(c.getClassId(), c.getClassName());
} }
} catch (Exception e) { } catch (Exception e) {
log.warn("[importStudents] 取班级名单失败(班级名留空): {}", e.getMessage()); log.warn("[dorm] 取班级名单失败(班级名留空): {}", e.getMessage());
} }
int added = 0; return new Roster(stus, classNames);
for (SchStudentDto s : stus) { }
if (s.getStuId() == null) {
@Override
public List<Map<String, Object>> availableStudents(String keyword) {
List<Map<String, Object>> out = new ArrayList<>();
Roster roster = fetchRoster();
if (roster == null) {
return out;
}
Set<Integer> 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; continue;
} }
if (studentRepository.findByStudentId(s.getStuId()).isPresent()) { Map<String, Object> m = new LinkedHashMap<>();
continue; // 已存在(在住或已在池)跳过,幂等 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<Integer> studentIds) {
if (studentIds == null || studentIds.isEmpty()) {
return 0;
}
Integer schId = UserContext.getSchId();
// 用最新在册名单补全姓名/班级(拿不到也不阻断,按 id 落库)
Map<Integer, SchStudentDto> rosterById = new HashMap<>();
Map<Integer, String> 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(); DormStudent d = new DormStudent();
d.setStudentId(s.getStuId()); d.setStudentId(id);
d.setStudentName(s.getStuName()); d.setStudentName(s != null ? s.getStuName() : null);
d.setClassName(classNames.get(s.getClassId())); d.setClassName(s != null ? classNames.get(s.getClassId()) : null);
d.setRoomId(0); d.setRoomId(0);
d.setIsMonitor(false); d.setIsMonitor(false);
d.setSourceDesc("学籍在册未分配床位"); d.setSourceDesc("学籍在册未分配床位");
@@ -424,10 +466,39 @@ public class DormService implements IDormService {
studentRepository.save(d); studentRepository.save(d);
added++; added++;
} }
log.info("[importStudents] schId={} schSerId={} 学籍 {} 人,新导入待安排 {} 人", schId, schSerId, stus.size(), added); log.info("[saveStudents] schId={} 勾选 {} 人,新增保存 {} 人", schId, studentIds.size(), added);
return added; return added;
} }
@Override
@Transactional
public int deleteStudents(List<Integer> studentIds) {
if (studentIds == null || studentIds.isEmpty()) {
return 0;
}
int removed = 0;
Set<Integer> roomsToRenumber = new HashSet<>();
for (Integer id : studentIds) {
if (id == null) {
continue;
}
Optional<DormStudent> 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<DormBuilding> buildingsOfSch() { private List<DormBuilding> buildingsOfSch() {