|
|
|
|
@ -35,45 +35,31 @@ public function generatePlans($dateRange, $planModelIds, $userId, $dateType = 1,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取部门ID (用于查重,取第一个模板的部门ID,假设批量操作通常针对同一部门)
|
|
|
|
|
// 如果业务允许跨部门混合勾选,这里可能需要调整查重逻辑(按部门分组查)
|
|
|
|
|
$department_id = $models->first()->department_id;
|
|
|
|
|
|
|
|
|
|
$start_date = new DateTime($dateRange[0]);
|
|
|
|
|
$end_date = new DateTime($dateRange[1]);
|
|
|
|
|
|
|
|
|
|
// ==========================================
|
|
|
|
|
// 【新增】1. 整体校验:模板与资源时令配置匹配
|
|
|
|
|
// 1. 整体校验:模板与资源时令配置匹配
|
|
|
|
|
// ==========================================
|
|
|
|
|
$this->validateTemplates($models);
|
|
|
|
|
|
|
|
|
|
// ==========================================
|
|
|
|
|
// 【新增】2. 整体校验:资源时段冲突检测
|
|
|
|
|
// 2. 整体校验:资源时段冲突检测
|
|
|
|
|
// ==========================================
|
|
|
|
|
$this->checkResourceTimeConflict($models, $start_date, $end_date);
|
|
|
|
|
|
|
|
|
|
// ==========================================
|
|
|
|
|
// 【核心】3. 重复性检测 (在事务外执行,提高性能)
|
|
|
|
|
// ==========================================
|
|
|
|
|
$this->checkDuplicateRecords(
|
|
|
|
|
$department_id,
|
|
|
|
|
$planModelIds,
|
|
|
|
|
$start_date,
|
|
|
|
|
$end_date,
|
|
|
|
|
$models
|
|
|
|
|
);
|
|
|
|
|
// 如果上面没抛异常,说明没有重复,继续往下执行
|
|
|
|
|
|
|
|
|
|
// 4. 获取节假日列表
|
|
|
|
|
// 3. 获取节假日列表
|
|
|
|
|
$holiday_list = DB::table('s_holiday')
|
|
|
|
|
->whereBetween('date', $dateRange)
|
|
|
|
|
->where(['type' => 2])
|
|
|
|
|
->pluck('date')
|
|
|
|
|
->toArray();
|
|
|
|
|
|
|
|
|
|
$generatedDates = [];
|
|
|
|
|
$skippedDates = [];
|
|
|
|
|
$success_count = 0;
|
|
|
|
|
|
|
|
|
|
// 5. 开启事务
|
|
|
|
|
// 4. 开启事务
|
|
|
|
|
DB::beginTransaction();
|
|
|
|
|
try {
|
|
|
|
|
$current_date = clone $start_date;
|
|
|
|
|
@ -85,12 +71,26 @@ public function generatePlans($dateRange, $planModelIds, $userId, $dateType = 1,
|
|
|
|
|
|
|
|
|
|
// 如果是"仅节假日"模式 (date_type == 2),且当天不是节假日 -> 跳过
|
|
|
|
|
if ($dateType == 2 && !in_array($current_date_str, $holiday_list)) {
|
|
|
|
|
foreach ($models as $model) {
|
|
|
|
|
$skippedDates[] = [
|
|
|
|
|
'date' => $current_date_str,
|
|
|
|
|
'template_id' => $model->id,
|
|
|
|
|
'reason' => '非节假日,跳过',
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
$current_date->modify('+1 day');
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果"节假日不可用" (HolidayEnable == 0),且当天是节假日 -> 跳过
|
|
|
|
|
if ($holidayEnable == 0 && in_array($current_date_str, $holiday_list)) {
|
|
|
|
|
foreach ($models as $model) {
|
|
|
|
|
$skippedDates[] = [
|
|
|
|
|
'date' => $current_date_str,
|
|
|
|
|
'template_id' => $model->id,
|
|
|
|
|
'reason' => '节假日不可用,跳过',
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
$current_date->modify('+1 day');
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
@ -108,10 +108,31 @@ public function generatePlans($dateRange, $planModelIds, $userId, $dateType = 1,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 【新增】时令校验:跳过不在时令范围内的日期
|
|
|
|
|
// 时令校验:跳过不在时令范围内的日期
|
|
|
|
|
$resource = $this->getCachedResource($model->resources_id);
|
|
|
|
|
if (!$this->isDateInSeasonalRange($current_date, $model->type, $resource)) {
|
|
|
|
|
continue; // 跳过该模板在此日期的生成
|
|
|
|
|
$skippedDates[] = [
|
|
|
|
|
'date' => $current_date_str,
|
|
|
|
|
'template_id' => $model->id,
|
|
|
|
|
'reason' => '不在时令范围',
|
|
|
|
|
];
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查当天该模板是否已有号源明细(存在即跳过)
|
|
|
|
|
$exists = DB::table('s_source_roster_detail')
|
|
|
|
|
->where('roster_id', $model->id)
|
|
|
|
|
->where('date', $current_date_str)
|
|
|
|
|
->where('is_del', 0)
|
|
|
|
|
->exists();
|
|
|
|
|
|
|
|
|
|
if ($exists) {
|
|
|
|
|
$skippedDates[] = [
|
|
|
|
|
'date' => $current_date_str,
|
|
|
|
|
'template_id' => $model->id,
|
|
|
|
|
'reason' => '已存在',
|
|
|
|
|
];
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- 构造插入数据 ---
|
|
|
|
|
@ -141,26 +162,36 @@ public function generatePlans($dateRange, $planModelIds, $userId, $dateType = 1,
|
|
|
|
|
if (!$plan_id) {
|
|
|
|
|
throw new Exception("号源明细插入失败,日期:{$current_date_str}, 模板ID:{$model->id}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$generatedDates[] = [
|
|
|
|
|
'date' => $current_date_str,
|
|
|
|
|
'template_id' => $model->id,
|
|
|
|
|
'detail_id' => $plan_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. 提交事务
|
|
|
|
|
// 5. 提交事务
|
|
|
|
|
DB::commit();
|
|
|
|
|
|
|
|
|
|
return ['success' => true, 'count' => $success_count];
|
|
|
|
|
return [
|
|
|
|
|
'success' => true,
|
|
|
|
|
'count' => $success_count,
|
|
|
|
|
'generated_dates' => $generatedDates,
|
|
|
|
|
'skipped_dates' => $skippedDates,
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
} catch (Exception $e) {
|
|
|
|
|
// 7. 异常回滚
|
|
|
|
|
// 6. 异常回滚
|
|
|
|
|
DB::rollBack();
|
|
|
|
|
// 记录日志
|
|
|
|
|
Log::error('Roster Generation Failed: ' . $e->getMessage(), [
|
|
|
|
|
@ -326,58 +357,6 @@ private function getTemplateDate($model)
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 独立的重复检测方法
|
|
|
|
|
* 如果发现重复,直接抛出包含详细信息的异常
|
|
|
|
|
*/
|
|
|
|
|
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 . '已存在相同记录,</br>存在于:</br>' . $msglist . '</br>对应记录Id为:' . $msgIds . '</br>请先删除后再操作';
|
|
|
|
|
|
|
|
|
|
// 抛出异常,中断流程
|
|
|
|
|
throw new Exception($fullErrorMessage);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function getWeekName($weekday) {
|
|
|
|
|
$map = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
|
|
|
|
|
return $map[$weekday] ?? '';
|
|
|
|
|
@ -429,4 +408,4 @@ private function insertDeviceInfo($detailId, $deviceIdStr, $modelId) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|