@@ -95,6 +95,8 @@ private InterviewAssignmentResultDTO assignInterviewsWithOptimization(
9595
9696 // 创建候选人列表,包含他们的偏好信息
9797 List <CandidateInfo > candidates = new ArrayList <>();
98+ List <InterviewAssignmentResultDTO .UnassignedUserDTO > unassignedUsers = new ArrayList <>();
99+
98100 for (Resume resume : resumes ) {
99101 User user = userService .getUserById (resume .getUserId ());
100102 if (user == null ) {
@@ -105,13 +107,29 @@ private InterviewAssignmentResultDTO assignInterviewsWithOptimization(
105107 List <String > preferredTimes = userPreferredTimes .getOrDefault (resume .getUserId (), new ArrayList <>());
106108 List <String > preferredDepartments = userPreferredDepartments .getOrDefault (resume .getUserId (), new ArrayList <>());
107109
108- // 如果用户没有填写期望面试时间或部门,跳过分配
109- if (preferredTimes .isEmpty () || preferredDepartments .isEmpty ()) {
110- logger .info ("用户 {} 没有填写期望面试时间或部门,跳过面试时间分配" , user .getUsername ());
110+ // 添加调试日志
111+ logger .info ("用户 {} 的期望面试时间: {}" , user .getUsername (), preferredTimes );
112+ logger .info ("用户 {} 的期望部门: {}" , user .getUsername (), preferredDepartments );
113+
114+ // 如果用户没有填写期望面试时间,则加入未分配列表
115+ if (preferredTimes .isEmpty ()) {
116+ logger .info ("用户 {} 没有填写期望面试时间,加入未分配列表" , user .getUsername ());
117+ unassignedUsers .add (new InterviewAssignmentResultDTO .UnassignedUserDTO (
118+ user .getUserId (), user .getUsername (), user .getName (), "未填写期望面试时间" ,
119+ preferredDepartments .isEmpty () ? "未填写期望部门" : String .join (", " , preferredDepartments )));
120+ continue ;
121+ }
122+
123+ // 如果用户没有填写期望部门,则加入未分配列表
124+ if (preferredDepartments .isEmpty ()) {
125+ logger .info ("用户 {} 没有填写期望部门,加入未分配列表" , user .getUsername ());
126+ unassignedUsers .add (new InterviewAssignmentResultDTO .UnassignedUserDTO (
127+ user .getUserId (), user .getUsername (), user .getName (),
128+ String .join (", " , preferredTimes ), "未填写期望部门" ));
111129 continue ;
112130 }
113131
114- String firstDepartment = preferredDepartments .isEmpty () ? "未指定部门" : preferredDepartments . get (0 );
132+ String firstDepartment = preferredDepartments .get (0 );
115133 candidates .add (new CandidateInfo (user , preferredTimes , preferredDepartments , firstDepartment ));
116134 }
117135
@@ -128,7 +146,6 @@ private InterviewAssignmentResultDTO assignInterviewsWithOptimization(
128146
129147 // 分配面试时间
130148 List <InterviewAssignmentResultDTO .AssignedInterviewDTO > assignedInterviews = new ArrayList <>();
131- List <InterviewAssignmentResultDTO .UnassignedUserDTO > unassignedUsers = new ArrayList <>();
132149
133150 for (CandidateInfo candidate : candidates ) {
134151 User user = candidate .user ;
@@ -143,8 +160,11 @@ private InterviewAssignmentResultDTO assignInterviewsWithOptimization(
143160 if (!assigned ) {
144161 String preferredTimesStr = String .join (", " , preferredTimes );
145162 String preferredDepartmentsStr = String .join (", " , candidate .preferredDepartments );
163+ logger .info ("用户 {} 未被分配,期望时间: {},期望部门: {}" , user .getUsername (), preferredTimesStr , preferredDepartmentsStr );
146164 unassignedUsers .add (new InterviewAssignmentResultDTO .UnassignedUserDTO (
147165 user .getUserId (), user .getUsername (), user .getName (), preferredTimesStr , preferredDepartmentsStr ));
166+ } else {
167+ logger .info ("用户 {} 已成功分配面试时间" , user .getUsername ());
148168 }
149169 }
150170
@@ -199,21 +219,30 @@ private Map<Integer, List<String>> getUserPreferredTimes(List<Resume> resumes, I
199219
200220 for (Resume resume : resumes ) {
201221 List <ResumeFieldValue > fieldValues = resumeService .getFieldValuesByResumeId (resume .getResumeId ());
202- Optional <ResumeFieldValue > interviewTimeValue = fieldValues .stream ()
222+ // 获取所有匹配的字段值
223+ List <ResumeFieldValue > interviewTimeValues = fieldValues .stream ()
203224 .filter (value -> fieldId .equals (value .getFieldId ()))
204- .findFirst ( );
225+ .collect ( Collectors . toList () );
205226
206- if (interviewTimeValue .isPresent ()) {
207- try {
208- // 解析JSON数组
209- List <String > preferredTimes = objectMapper .readValue (
210- interviewTimeValue .get ().getFieldValue (),
211- new TypeReference <List <String >>() {});
212- userPreferredTimes .put (resume .getUserId (), preferredTimes );
213- } catch (Exception e ) {
214- logger .warn ("解析用户 {} 的期望面试时间失败: {}" , resume .getUserId (),
215- interviewTimeValue .get ().getFieldValue (), e );
227+ logger .info ("用户 {} 的期望面试时间字段值数量: {}" , resume .getUserId (), interviewTimeValues .size ());
228+
229+ if (!interviewTimeValues .isEmpty ()) {
230+ List <String > allPreferredTimes = new ArrayList <>();
231+ for (ResumeFieldValue interviewTimeValue : interviewTimeValues ) {
232+ logger .info ("用户 {} 的期望面试时间字段值: {}" , resume .getUserId (), interviewTimeValue .getFieldValue ());
233+ try {
234+ // 解析JSON数组
235+ List <String > preferredTimes = objectMapper .readValue (
236+ interviewTimeValue .getFieldValue (),
237+ new TypeReference <List <String >>() {});
238+ allPreferredTimes .addAll (preferredTimes );
239+ } catch (Exception e ) {
240+ logger .warn ("解析用户 {} 的期望面试时间失败: {}" , resume .getUserId (),
241+ interviewTimeValue .getFieldValue (), e );
242+ }
216243 }
244+ logger .info ("用户 {} 解析后的所有期望面试时间: {}" , resume .getUserId (), allPreferredTimes );
245+ userPreferredTimes .put (resume .getUserId (), allPreferredTimes );
217246 }
218247 }
219248
@@ -228,21 +257,30 @@ private Map<Integer, List<String>> getUserPreferredDepartments(List<Resume> resu
228257
229258 for (Resume resume : resumes ) {
230259 List <ResumeFieldValue > fieldValues = resumeService .getFieldValuesByResumeId (resume .getResumeId ());
231- Optional <ResumeFieldValue > expectedDepartmentsValue = fieldValues .stream ()
260+ // 获取所有匹配的字段值
261+ List <ResumeFieldValue > expectedDepartmentsValues = fieldValues .stream ()
232262 .filter (value -> fieldId .equals (value .getFieldId ()))
233- .findFirst ();
263+ .collect (Collectors .toList ());
264+
265+ logger .info ("用户 {} 的期望部门字段值数量: {}" , resume .getUserId (), expectedDepartmentsValues .size ());
234266
235- if (expectedDepartmentsValue .isPresent ()) {
236- try {
237- // 解析JSON数组
238- List <String > preferredDepartments = objectMapper .readValue (
239- expectedDepartmentsValue .get ().getFieldValue (),
240- new TypeReference <List <String >>() {});
241- userPreferredDepartments .put (resume .getUserId (), preferredDepartments );
242- } catch (Exception e ) {
243- logger .warn ("解析用户 {} 的期望部门失败: {}" , resume .getUserId (),
244- expectedDepartmentsValue .get ().getFieldValue (), e );
267+ if (!expectedDepartmentsValues .isEmpty ()) {
268+ List <String > allPreferredDepartments = new ArrayList <>();
269+ for (ResumeFieldValue expectedDepartmentsValue : expectedDepartmentsValues ) {
270+ logger .info ("用户 {} 的期望部门字段值: {}" , resume .getUserId (), expectedDepartmentsValue .getFieldValue ());
271+ try {
272+ // 解析JSON数组
273+ List <String > preferredDepartments = objectMapper .readValue (
274+ expectedDepartmentsValue .getFieldValue (),
275+ new TypeReference <List <String >>() {});
276+ allPreferredDepartments .addAll (preferredDepartments );
277+ } catch (Exception e ) {
278+ logger .warn ("解析用户 {} 的期望部门失败: {}" , resume .getUserId (),
279+ expectedDepartmentsValue .getFieldValue (), e );
280+ }
245281 }
282+ logger .info ("用户 {} 解析后的所有期望部门: {}" , resume .getUserId (), allPreferredDepartments );
283+ userPreferredDepartments .put (resume .getUserId (), allPreferredDepartments );
246284 }
247285 }
248286
@@ -259,12 +297,12 @@ private List<LocalDateTime> generateTimeSlotsForSpecificDays(LocalDate day1, Loc
259297 LocalDate [] dates = {day1 , day2 };
260298 for (LocalDate date : dates ) {
261299 // 生成上午时间段 (9:00-11:00)
262- for (LocalTime time = MORNING_START ; time .isBefore (MORNING_END ); time = time .plusMinutes (INTERVIEW_DURATION )) {
300+ for (LocalTime time = MORNING_START ; ! time .equals (MORNING_END ); time = time .plusMinutes (INTERVIEW_DURATION )) {
263301 timeSlots .add (LocalDateTime .of (date , time ));
264302 }
265303
266304 // 生成下午时间段 (13:00-17:00)
267- for (LocalTime time = AFTERNOON_START ; time .isBefore (AFTERNOON_END ); time = time .plusMinutes (INTERVIEW_DURATION )) {
305+ for (LocalTime time = AFTERNOON_START ; ! time .equals (AFTERNOON_END ); time = time .plusMinutes (INTERVIEW_DURATION )) {
268306 timeSlots .add (LocalDateTime .of (date , time ));
269307 }
270308 }
@@ -283,12 +321,12 @@ private List<LocalDateTime> generateTimeSlots(LocalDate startDate, LocalDate end
283321 LocalDate date = startDate .plusDays (dayOffset );
284322
285323 // 生成上午时间段 (9:00-11:00)
286- for (LocalTime time = MORNING_START ; time .isBefore (MORNING_END ); time = time .plusMinutes (INTERVIEW_DURATION )) {
324+ for (LocalTime time = MORNING_START ; ! time .equals (MORNING_END ); time = time .plusMinutes (INTERVIEW_DURATION )) {
287325 timeSlots .add (LocalDateTime .of (date , time ));
288326 }
289327
290328 // 生成下午时间段 (13:00-17:00)
291- for (LocalTime time = AFTERNOON_START ; time .isBefore (AFTERNOON_END ); time = time .plusMinutes (INTERVIEW_DURATION )) {
329+ for (LocalTime time = AFTERNOON_START ; ! time .equals (AFTERNOON_END ); time = time .plusMinutes (INTERVIEW_DURATION )) {
292330 timeSlots .add (LocalDateTime .of (date , time ));
293331 }
294332 }
@@ -344,22 +382,62 @@ private boolean tryAssignInterviewTime(User user, List<String> preferredTimes, S
344382 return false ;
345383 }
346384
385+ logger .info ("开始为用户 {} 分配面试时间,期望时间: {}" , user .getUsername (), preferredTimes );
386+
347387 // 按照用户期望的时间顺序尝试分配
348388 for (String preferredTime : preferredTimes ) {
349- LocalDateTime assignedSlot = findAndReserveSlot (preferredTime , department , departmentSlotAvailability );
350- if (assignedSlot != null ) {
351- // 成功分配时间
352- String period = assignedSlot .getHour () < 12 ? "上午" : "下午" ;
353- assignedInterviews .add (new InterviewAssignmentResultDTO .AssignedInterviewDTO (
354- user .getUserId (), user .getUsername (), user .getName (), assignedSlot , period , department ));
355- return true ;
389+ logger .info ("尝试为用户 {} 分配时间: {}" , user .getUsername (), preferredTime );
390+ // 检查该时间段是否有空位
391+ if (hasAvailableSlot (preferredTime , department , departmentSlotAvailability )) {
392+ logger .info ("用户 {} 的时间段 {} 有空位,正在分配" , user .getUsername (), preferredTime );
393+ // 如果有空位,则分配时间
394+ LocalDateTime assignedSlot = findAndReserveSlot (preferredTime , department , departmentSlotAvailability );
395+ if (assignedSlot != null ) {
396+ // 成功分配时间
397+ String period = assignedSlot .getHour () < 12 ? "上午" : "下午" ;
398+ logger .info ("成功为用户 {} 分配面试时间: {}" , user .getUsername (), assignedSlot );
399+ assignedInterviews .add (new InterviewAssignmentResultDTO .AssignedInterviewDTO (
400+ user .getUserId (), user .getUsername (), user .getName (), assignedSlot , period , department ));
401+ return true ;
402+ }
403+ } else {
404+ logger .info ("用户 {} 的时间段 {} 没有空位,尝试下一个时间段" , user .getUsername (), preferredTime );
356405 }
357406 }
358407
359- // 如果按照用户期望的时间无法分配,则将用户加入未分配列表
360- // 不再尝试分配任何可用的时间,确保只在用户期望的时间段内分配
408+ // 如果用户选择的所有时间段都没有空位,则不分配时间
409+ logger .info ("用户 {} 的所有期望时间段都没有空位,无法分配" , user .getUsername ());
410+ return false ;
411+ }
412+
413+ /**
414+ * 检查指定时间段是否还有空位
415+ */
416+ private boolean hasAvailableSlot (String preferredTime , String department ,
417+ Map <String , Map <LocalDateTime , Boolean >> departmentSlotAvailability ) {
418+ Map <LocalDateTime , Boolean > slotAvailability = departmentSlotAvailability .get (department );
419+ if (slotAvailability == null ) {
420+ return false ;
421+ }
422+
423+ // 获取所有可用的时间槽
424+ List <LocalDateTime > availableSlots = slotAvailability .entrySet ().stream ()
425+ .filter (Map .Entry ::getValue ) // 只考虑可用的时间槽
426+ .map (Map .Entry ::getKey )
427+ .sorted ()
428+ .collect (Collectors .toList ());
429+
430+ logger .debug ("部门 {} 的可用时间槽: {}" , department , availableSlots );
361431
362- return false ; // 无法分配时间
432+ // 检查是否有符合期望时间段的时间槽
433+ for (LocalDateTime slotTime : availableSlots ) {
434+ if (isSlotMatchPreference (slotTime , preferredTime )) {
435+ logger .debug ("找到匹配的时间槽: {} 对应偏好时间: {}" , slotTime , preferredTime );
436+ return true ;
437+ }
438+ }
439+
440+ return false ;
363441 }
364442
365443 /**
@@ -384,6 +462,7 @@ private LocalDateTime findAndReserveSlot(String preferredTime, String department
384462 if (isSlotMatchPreference (slotTime , preferredTime )) {
385463 // 预留该时间槽
386464 slotAvailability .put (slotTime , false );
465+ logger .info ("为部门 {} 预留时间槽: {}" , department , slotTime );
387466 return slotTime ;
388467 }
389468 }
@@ -419,6 +498,7 @@ private LocalDateTime findAnyAvailableSlot(String department,
419498
420499 /**
421500 * 检查时间槽是否符合用户的期望时间段
501+ * 严格按照用户选择的具体日期和时间段进行匹配
422502 */
423503 private boolean isSlotMatchPreference (LocalDateTime slotTime , String preferredTime ) {
424504 String [] parts = preferredTime .split (" " );
@@ -430,9 +510,20 @@ private boolean isSlotMatchPreference(LocalDateTime slotTime, String preferredTi
430510 int dayNumber = Integer .parseInt (parts [1 ]);
431511 String period = parts [2 ]; // "上午" 或 "下午"
432512
433- // 判断是第几天 (假设面试从招募周期开始日期算起)
434- // 注意:这里简化处理,实际应该根据开始日期计算
435- if (dayNumber == 1 || dayNumber == 2 ) {
513+ // 根据面试时间槽的日期确定是第几天
514+ LocalDate day1 = LocalDate .of (2025 , 9 , 27 );
515+ LocalDate day2 = LocalDate .of (2025 , 9 , 28 );
516+
517+ // 严格匹配具体日期和时间段
518+ if (dayNumber == 1 && slotTime .toLocalDate ().equals (day1 )) {
519+ // 检查时间段是否匹配
520+ LocalTime time = slotTime .toLocalTime ();
521+ if ("上午" .equals (period )) {
522+ return !time .isBefore (MORNING_START ) && time .isBefore (MORNING_END );
523+ } else if ("下午" .equals (period )) {
524+ return !time .isBefore (AFTERNOON_START ) && time .isBefore (AFTERNOON_END );
525+ }
526+ } else if (dayNumber == 2 && slotTime .toLocalDate ().equals (day2 )) {
436527 // 检查时间段是否匹配
437528 LocalTime time = slotTime .toLocalTime ();
438529 if ("上午" .equals (period )) {
0 commit comments