You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

259 lines
9.9 KiB
PHP

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
namespace App\Services\Admin\YeWu;
use DateTime;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Exception;
class RosterService
{
public function generatePlans($dateRange, $planModelIds, $userId, $dateType = 1, $holidayEnable = 1)
{
// 1. 基础参数校验
if (!is_array($dateRange) || count($dateRange) != 2 || empty($planModelIds)) {
throw new Exception('日期范围或模板ID参数错误');
}
// 2. 获取模板信息并校验状态
$models = DB::table('s_source_roster')
->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 . '已存在相同记录,</br>存在于:</br>' . $msglist . '</br>对应记录Id为' . $msgIds . '</br>请先删除后再操作';
// 抛出异常,中断流程
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("设备关联创建失败");
}
}
}
}