diff --git a/Laravel/app/Http/Controllers/API/Admin/YeWu/PlanListController.php b/Laravel/app/Http/Controllers/API/Admin/YeWu/PlanListController.php index d802603..94478a2 100644 --- a/Laravel/app/Http/Controllers/API/Admin/YeWu/PlanListController.php +++ b/Laravel/app/Http/Controllers/API/Admin/YeWu/PlanListController.php @@ -34,8 +34,12 @@ public function Create(Request $request,RosterService $rosterService) $HolidayEnable ); - return \Yz::Return(true, '执行完成,共计生成计划 ' . $result['count'] . ' 条', $result); + // 检查是否返回了冲突数据 + if (!empty($result['conflicts'])) { + return \Yz::Return(false, '存在时段冲突,生成失败,共 ' . count($result['conflicts']) . ' 条冲突', $result); + } + return \Yz::Return(true, '执行完成,共计生成计划 ' . $result['count'] . ' 条', $result); } catch (\Exception $e) { // 捕获服务层抛出的异常,格式化返回给前端 return \Yz::echoError1($e->getMessage()); @@ -798,6 +802,11 @@ public function CreateAdmin(Request $request, RosterService $rosterService) $date_type, $HolidayEnable ); + // 检查是否返回了冲突数据 + if (!empty($result['conflicts'])) { + return \Yz::Return(false, '存在时段冲突,生成失败,共 ' . count($result['conflicts']) . ' 条冲突', $result); + } + return \Yz::Return(true, '执行完成,共计生成计划 ' . $result['count'] . ' 条', $result); } catch (\Exception $e) { return \Yz::echoError1($e->getMessage()); diff --git a/Laravel/app/Services/Admin/YeWu/RosterService.php b/Laravel/app/Services/Admin/YeWu/RosterService.php index b4de125..342f1fe 100644 --- a/Laravel/app/Services/Admin/YeWu/RosterService.php +++ b/Laravel/app/Services/Admin/YeWu/RosterService.php @@ -43,18 +43,27 @@ public function generatePlans($dateRange, $planModelIds, $userId, $dateType = 1, // ========================================== $this->validateTemplates($models); - // ========================================== - // 2. 整体校验:资源时段冲突检测 - // ========================================== - $this->checkResourceTimeConflict($models, $start_date, $end_date); - - // 3. 获取节假日列表 + // 2. 获取节假日列表 $holiday_list = DB::table('s_holiday') ->whereBetween('date', $dateRange) ->where(['type' => 2]) ->pluck('date') ->toArray(); + // ========================================== + // 3. 整体校验:资源时段冲突检测(先检查再生成) + // ========================================== + $conflicts = $this->checkResourceTimeConflict( + $models, $start_date, $end_date, $dateType, $holidayEnable, $holiday_list + ); + + if (!empty($conflicts)) { + return [ + 'success' => false, + 'conflicts' => $conflicts, + ]; + } + $generatedDates = []; $skippedDates = []; $success_count = 0; @@ -95,26 +104,6 @@ public function generatePlans($dateRange, $planModelIds, $userId, $dateType = 1, continue; } - // --- 资源级存在性检查:当天该资源下是否有任何号源 --- - $resources_id = $models->first()->resources_id; - $resourceExists = DB::table('s_source_roster_detail') - ->where('resources_id', $resources_id) - ->where('date', $current_date_str) - ->where('is_del', 0) - ->exists(); - - if ($resourceExists) { - foreach ($models as $model) { - $skippedDates[] = [ - 'date' => $current_date_str, - 'template_id' => $model->id, - 'reason' => '已存在', - ]; - } - $current_date->modify('+1 day'); - continue; - } - // --- 获取星期 --- $weekday = (int)$current_date->format('w'); $weekname = $this->getWeekName($weekday); @@ -238,45 +227,77 @@ private function validateTemplates($models) } /** - * 资源级别查重:同一资源同一天的时间段不能重叠 - * 同一资源同一天可以有多个不同时段,但是时段不能交差 + * 资源级别冲突检测:遍历日期范围内每一天,应用与主循环一致的过滤逻辑, + * 检查每个模板的资源在该日期是否存在时段重叠的号源。 * 边界判断:不重叠(一个时间点结束,另一个可以开始) + * 返回冲突列表,无冲突则返回空数组 */ - private function checkResourceTimeConflict($models, $startDate, $endDate) + private function checkResourceTimeConflict($models, $startDate, $endDate, $dateType, $holidayEnable, $holidayList) { - $startStr = $startDate->format('Y-m-d'); - $endStr = $endDate->format('Y-m-d'); + $conflicts = []; + $currentDate = clone $startDate; - foreach ($models as $model) { - // 查询该资源在日期范围内已存在的排班 - $existingPlans = DB::table('s_source_roster_detail') - ->where('resources_id', $model->resources_id) - ->where('date', '>=', $startStr) - ->where('date', '<=', $endStr) - ->where('is_del', 0) - ->get(); - - foreach ($existingPlans as $existing) { - // 检查时间是否重叠(同一天才检查) - $existingDate = $existing->date; - // 使用模板的星期作为参考日期 - $templateDate = $this->getTemplateDate($model); - if ($existingDate === $templateDate) { + while ($currentDate <= $endDate) { + $dateStr = $currentDate->format('Y-m-d'); + + // --- 节假日过滤(与主循环一致) --- + if ($dateType == 2 && !in_array($dateStr, $holidayList)) { + $currentDate->modify('+1 day'); + continue; + } + if ($holidayEnable == 0 && in_array($dateStr, $holidayList)) { + $currentDate->modify('+1 day'); + continue; + } + + $weekday = (int)$currentDate->format('w'); + $weekname = $this->getWeekName($weekday); + + foreach ($models as $model) { + // --- 星期匹配(与主循环一致) --- + if ($dateType == 1 && isset($model->date_type) && $model->date_type == 1) { + if ($model->weekname !== $weekname) { + continue; + } + } + + // --- 时令校验(与主循环一致) --- + $resource = $this->getCachedResource($model->resources_id); + if (!$this->isDateInSeasonalRange($currentDate, $model->type, $resource)) { + continue; + } + + // --- 查询该资源在该日期下已有的号源,检查时间段是否重叠 --- + $existingPlans = DB::table('s_source_roster_detail') + ->where('resources_id', $model->resources_id) + ->where('date', $dateStr) + ->where('is_del', 0) + ->get(); + + foreach ($existingPlans as $existing) { if ($this->isTimeOverlap( $model->begin_time, $model->end_time, $existing->begin_time, $existing->end_time )) { - throw new Exception( - "资源ID [{$model->resources_id}] 在 {$existingDate} 存在时段冲突!" . - "新增: {$model->begin_time}-{$model->end_time} " . - "与已存在的 {$existing->begin_time}-{$existing->end_time} 重叠" - ); + $conflicts[] = [ + 'date' => $dateStr, + 'weekname' => $weekname, + 'template_id' => $model->id, + 'new_time' => $model->begin_time . '-' . $model->end_time, + 'existing_time' => $existing->begin_time . '-' . $existing->end_time, + 'existing_detail_id' => $existing->id, + 'resources_id' => $model->resources_id, + ]; } } } + + $currentDate->modify('+1 day'); } + + return $conflicts; } /** @@ -347,20 +368,6 @@ private function getCachedResource($resourceId) return $this->resourceCache[$resourceId]; } - /** - * 获取模板的日期(用于资源查重时的日期比较) - * 注意:这里仅用于比较,实际生成时按循环中的日期 - */ - private function getTemplateDate($model) - { - // 如果模板有固定日期,返回固定日期 - if (isset($model->date)) { - return $model->date; - } - // 否则返回 null(表示需要按循环日期比较) - return null; - } - private function getWeekName($weekday) { $map = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']; return $map[$weekday] ?? '';