whereIn('id', $planModelIds) ->get(); if ($models->isEmpty()) { throw new Exception('未找到有效的排班模板'); } foreach ($models as $model) { if ($model->status != 1 || $model->is_del != 0) { throw new Exception("模板状态异常,请重新选择!异常模板Id: {$model->id}"); } } // 获取部门ID (用于查重,取第一个模板的部门ID,假设批量操作通常针对同一部门) // 如果业务允许跨部门混合勾选,这里可能需要调整查重逻辑(按部门分组查) $department_id = $models->first()->department_id; $start_date = new DateTime($dateRange[0]); $end_date = new DateTime($dateRange[1]); // ========================================== // 【核心】3. 重复性检测 (在事务外执行,提高性能) // ========================================== $this->checkDuplicateRecords( $department_id, $planModelIds, $start_date, $end_date, $models ); // 如果上面没抛异常,说明没有重复,继续往下执行 // 4. 获取节假日列表 $holiday_list = DB::table('s_holiday') ->whereBetween('date', $dateRange) ->where(['type' => 2]) ->pluck('date') ->toArray(); $success_count = 0; // 5. 开启事务 DB::beginTransaction(); try { $current_date = clone $start_date; while ($current_date <= $end_date) { $current_date_str = $current_date->format('Y-m-d'); // --- 逻辑判断:节假日过滤 --- // 如果是“仅节假日”模式 (date_type == 2),且当天不是节假日 -> 跳过 if ($dateType == 2 && !in_array($current_date_str, $holiday_list)) { $current_date->modify('+1 day'); continue; } // 如果“节假日不可用” (HolidayEnable == 0),且当天是节假日 -> 跳过 if ($holidayEnable == 0 && in_array($current_date_str, $holiday_list)) { $current_date->modify('+1 day'); continue; } // --- 获取星期 --- $weekday = (int)$current_date->format('w'); $weekname = $this->getWeekName($weekday); foreach ($models as $model) { // --- 逻辑判断:星期匹配 --- // 如果是“按星期”模式 (date_type == 1) 且模板也是按星期定义的,必须星期一致 if ($dateType == 1 && isset($model->date_type) && $model->date_type == 1) { if ($model->weekname !== $weekname) { continue; } } // --- 构造插入数据 --- $data = [ 'roster_id' => $model->id, 'date' => $current_date_str, 'weekname' => $weekname, 'department_id' => $model->department_id, 'resources_id' => $model->resources_id ?? null, 'device_id' => $model->device_id ?? null, 'period_id' => $model->period_id ?? null, 'patient_type' => $model->patient_type ?? null, 'begin_time' => $model->begin_time, 'end_time' => $model->end_time, 'end_reservation_time' => $model->end_reservation_time ?? null, 'time_unit' => $model->time_unit ?? null, 'status' => 1, 'adduser' => $userId, 'is_del' => 0, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'), ]; // --- 插入主表 --- $plan_id = DB::table('s_source_roster_detail')->insertGetId($data); if (!$plan_id) { throw new Exception("号源明细插入失败,日期:{$current_date_str}, 模板ID:{$model->id}"); } $success_count++; // --- 插入关联表:数量配置 --- $this->insertCountInfo($plan_id, $model->id); // --- 插入关联表:设备配置 --- // 注意:原逻辑如果 device_id 为空会报错,这里保持原逻辑 $this->insertDeviceInfo($plan_id, $model->device_id, $model->id); } $current_date->modify('+1 day'); } // 6. 提交事务 DB::commit(); return ['success' => true, 'count' => $success_count]; } catch (Exception $e) { // 7. 异常回滚 DB::rollBack(); // 记录日志 Log::error('Roster Generation Failed: ' . $e->getMessage(), [ 'dateRange' => $dateRange, 'user_id' => $userId ]); // 重新抛出,让 Controller 处理 throw $e; } } /** * 独立的重复检测方法 * 如果发现重复,直接抛出包含详细信息的异常 */ private function checkDuplicateRecords($department_id, $planModelIds, $startDate, $endDate, $models) { $startStr = $startDate->format('Y-m-d'); $endStr = $endDate->format('Y-m-d'); // 查询已存在的记录 $checkList = DB::table('s_source_roster_detail') ->where('department_id', $department_id) ->whereIn('roster_id', $planModelIds) ->where('date', '>=', $startStr) ->where('date', '<=', $endStr) ->where('is_del', 0) ->get(); if ($checkList->isNotEmpty()) { // 构造详细的错误提示信息 (完全还原你原代码的逻辑) $msg = '已有重复的计划明细,禁止创建!当前选中的'; $msglist = ''; $msgIds = ''; // 优化:将检查结果转为映射数组,避免双重循环 O(N*M) // key: roster_id, value: array of items $checkMap = []; foreach ($checkList as $item) { if (!isset($checkMap[$item->roster_id])) { $checkMap[$item->roster_id] = []; } $checkMap[$item->roster_id][] = $item; } foreach ($models as $model) { if (isset($checkMap[$model->id])) { foreach ($checkMap[$model->id] as $item) { $msglist .= $item->date . ' '; $msgIds .= $item->id . ' '; // 拼接模板信息 $msg .= " " . $model->weekname . $model->begin_time . '-' . $model->end_time . " "; } } } $fullErrorMessage = $msg . '已存在相同记录,
存在于:
' . $msglist . '
对应记录Id为:' . $msgIds . '
请先删除后再操作'; // 抛出异常,中断流程 throw new Exception($fullErrorMessage); } } private function getWeekName($weekday) { $map = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']; return $map[$weekday] ?? ''; } private function insertCountInfo($detailId, $rosterId) { $model_count_info = DB::table('s_source_roster_count')->where(['roster_id' => $rosterId])->get(); if ($model_count_info->isEmpty()) { throw new Exception("模板数量信息异常,请重新选择!异常模板Id: {$rosterId}"); } foreach ($model_count_info as $info) { $success = DB::table('s_source_roster_detail_count')->insert([ 'roster_detail_id' => $detailId, 'appointment_type_id' => $info->appointment_type_id, 'count' => $info->count, 'max_total' => $info->max_total, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'), ]); if (!$success) { throw new Exception("渠道数量创建失败"); } } } private function insertDeviceInfo($detailId, $deviceIdStr, $modelId) { // 保持原逻辑:如果模板没配设备,视为异常 if (empty($deviceIdStr)) { throw new Exception("模板未关联设备,请重新选择!异常模板Id: {$modelId}"); } $device_ids = explode(",", $deviceIdStr); foreach ($device_ids as $dv_value) { $dv_value = trim($dv_value); if ($dv_value === '') continue; $success = DB::table('s_source_roster_detail_device')->insert([ 'roster_detail_id' => $detailId, 'device_id' => $dv_value, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'), ]); if (!$success) { throw new Exception("设备关联创建失败"); } } } }