|
|
<?php
|
|
|
|
|
|
namespace App\Http\Controllers\API\Admin;
|
|
|
|
|
|
use App\Http\Controllers\Controller;
|
|
|
use App\Services\UserService;
|
|
|
use Illuminate\Http\Request;
|
|
|
use App\Lib\Rs;
|
|
|
use Illuminate\Support\Facades\DB;
|
|
|
use Illuminate\Validation\Rule;
|
|
|
|
|
|
class ModelController extends Controller
|
|
|
{
|
|
|
private $userService;
|
|
|
|
|
|
public function __construct(UserService $userService)
|
|
|
{
|
|
|
$this->userService = $userService;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 获取号源模板列表
|
|
|
* @param Request $request
|
|
|
* @return \Illuminate\Http\JsonResponse
|
|
|
*/
|
|
|
public function list(Request $request)
|
|
|
{
|
|
|
$payload = $request->attributes->get('payload');
|
|
|
if (!$payload) {
|
|
|
return Rs::error('用户信息无效', 401);
|
|
|
}
|
|
|
|
|
|
$user = $this->userService->getUserInfoFromPayload($payload);
|
|
|
if (!$user) {
|
|
|
return Rs::error('用户不存在', 401);
|
|
|
}
|
|
|
|
|
|
$isAdmin = $this->userService->isAdmin($payload);
|
|
|
$userDeptId = $user->dept_id ?? null;
|
|
|
|
|
|
$departmentId = $request->input('department_id', null);
|
|
|
$modelTypeId = $request->input('model_type_id', null);
|
|
|
$resourceId = $request->input('resource_id', null);
|
|
|
$slotMode = $request->input('slot_mode', null);
|
|
|
$weekname = $request->input('weekname', null);
|
|
|
$page = $request->input('page', 1);
|
|
|
$pageSize = $request->input('page_size', 10);
|
|
|
|
|
|
$query = DB::table('plan_model')
|
|
|
->leftJoin('plan_model_type', 'plan_model.model_type_id', '=', 'plan_model_type.id')
|
|
|
->leftJoin('department', 'plan_model.department_id', '=', 'department.id')
|
|
|
->leftJoin('department_resource', 'plan_model.resource_id', '=', 'department_resource.id')
|
|
|
->where('plan_model.deleted', 0)
|
|
|
->select(
|
|
|
'plan_model.id',
|
|
|
'plan_model.model_type_id',
|
|
|
'plan_model_type.name as model_type_name',
|
|
|
'plan_model.department_id',
|
|
|
'department.name as department_name',
|
|
|
'plan_model.resource_id',
|
|
|
'department_resource.name as resource_name',
|
|
|
'department_resource.slot_mode',
|
|
|
'plan_model.total_quota',
|
|
|
'plan_model.weekname',
|
|
|
'plan_model.patient_type_mask',
|
|
|
'plan_model.start_time',
|
|
|
'plan_model.end_time',
|
|
|
'plan_model.cutoff_time',
|
|
|
'plan_model.status',
|
|
|
'plan_model.created_at',
|
|
|
'plan_model.updated_at'
|
|
|
);
|
|
|
|
|
|
// 权限控制:非管理员只能查看自己科室的模板
|
|
|
if (!$isAdmin) {
|
|
|
if (empty($userDeptId)) {
|
|
|
return Rs::error('请先绑定科室');
|
|
|
}
|
|
|
$query->where('plan_model.department_id', $userDeptId);
|
|
|
// 如果用户传入了科室参数,需要验证是否属于自己科室
|
|
|
if ($departmentId !== null && $departmentId !== '') {
|
|
|
if ($departmentId != $userDeptId) {
|
|
|
return Rs::error('无权限访问其他科室的数据');
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if ($modelTypeId !== null && $modelTypeId !== '') {
|
|
|
$query->where('plan_model.model_type_id', $modelTypeId);
|
|
|
}
|
|
|
|
|
|
if ($slotMode !== null && $slotMode !== '') {
|
|
|
$query->where('plan_model.slot_mode', $slotMode);
|
|
|
}
|
|
|
|
|
|
if ($resourceId !== null && $resourceId !== '') {
|
|
|
$query->where('plan_model.resource_id', $resourceId);
|
|
|
}
|
|
|
|
|
|
if ($weekname !== null && $weekname !== '') {
|
|
|
$query->where('plan_model.weekname', $weekname);
|
|
|
}
|
|
|
|
|
|
if ($departmentId !== null && $departmentId !== '' && $isAdmin) {
|
|
|
// 管理员或无科室限制的用户可以根据科室筛选
|
|
|
$query->where('plan_model.department_id', $departmentId);
|
|
|
}
|
|
|
|
|
|
$total = $query->count();
|
|
|
|
|
|
$models = $query->orderBy('plan_model.id', 'desc')
|
|
|
->skip(($page - 1) * $pageSize)
|
|
|
->take($pageSize)
|
|
|
->get();
|
|
|
|
|
|
// 获取每个模板的渠道配额信息(包含公共池)
|
|
|
foreach ($models as $model) {
|
|
|
$channelQuotas = DB::table('plan_model_channel_quota as pmcq')
|
|
|
->leftJoin('channel', 'pmcq.channel_id', '=', 'channel.id')
|
|
|
->where('pmcq.model_id', $model->id)
|
|
|
->where('pmcq.deleted', 0)
|
|
|
->select(
|
|
|
'pmcq.id',
|
|
|
'pmcq.channel_id',
|
|
|
'channel.name as channel_name',
|
|
|
'channel.short_name',
|
|
|
'pmcq.total_quota',
|
|
|
'pmcq.allow_overuse_public_pool'
|
|
|
)
|
|
|
->get();
|
|
|
|
|
|
$model->channel_quotas = $channelQuotas;
|
|
|
}
|
|
|
|
|
|
$data = [
|
|
|
'list' => $models,
|
|
|
'total' => $total,
|
|
|
'page' => $page,
|
|
|
'page_size' => $pageSize,
|
|
|
'total_pages' => ceil($total / $pageSize)
|
|
|
];
|
|
|
|
|
|
return Rs::success($data);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 保存号源模板
|
|
|
* @param Request $request
|
|
|
* @return \Illuminate\Http\JsonResponse
|
|
|
*/
|
|
|
public function Save(Request $request)
|
|
|
{
|
|
|
$payload = $request->attributes->get('payload');
|
|
|
if (!$payload) {
|
|
|
return Rs::error('用户信息无效', 401);
|
|
|
}
|
|
|
|
|
|
$user = $this->userService->getUserInfoFromPayload($payload);
|
|
|
if (!$user) {
|
|
|
return Rs::error('用户不存在', 401);
|
|
|
}
|
|
|
|
|
|
$isAdmin = $this->userService->isAdmin($payload);
|
|
|
$userDeptId = $user->dept_id ?? null;
|
|
|
|
|
|
$data = $request->validate([
|
|
|
'id' => 'nullable|integer',
|
|
|
'model_type_id' => 'required|integer',
|
|
|
'department_id' => 'required|integer',
|
|
|
'resource_id' => 'required|integer',
|
|
|
'slot_mode' => 'nullable|integer|in:1,2',
|
|
|
'total_quota' => 'required|integer|min:0',
|
|
|
'weekname' => 'required|string',
|
|
|
'patient_type_mask' => 'required|integer|min:1',
|
|
|
'start_time' => 'required|date_format:H:i:s',
|
|
|
'end_time' => 'required|date_format:H:i:s',
|
|
|
'cutoff_time' => 'nullable|date_format:H:i:s',
|
|
|
'allow_overuse_public_pool' => 'nullable|integer|in:0,1',
|
|
|
'status' => 'required|integer|in:0,1',
|
|
|
'channel_quotas' => 'nullable|array',
|
|
|
'channel_quotas.*.channel_id' => 'nullable|integer',
|
|
|
'channel_quotas.*.total_quota' => 'nullable|integer|min:0',
|
|
|
'channel_quotas.*.allow_overuse_public_pool' => 'nullable|integer|in:0,1',
|
|
|
]);
|
|
|
|
|
|
// 如果没有 id 或 id 为空,默认为 0(新建)
|
|
|
if (!isset($data['id']) || $data['id'] === null) {
|
|
|
$data['id'] = 0;
|
|
|
}
|
|
|
|
|
|
// 验证时间逻辑
|
|
|
$startTime = strtotime('2000-01-01 ' . $data['start_time']);
|
|
|
$endTime = strtotime('2000-01-01 ' . $data['end_time']);
|
|
|
|
|
|
if ($endTime <= $startTime) {
|
|
|
return Rs::error('结束时间必须晚于开始时间');
|
|
|
}
|
|
|
|
|
|
// 如果没有设置截止预约时间,默认使用结束时间
|
|
|
if (empty($data['cutoff_time'])) {
|
|
|
$data['cutoff_time'] = $data['end_time'];
|
|
|
}
|
|
|
|
|
|
// 如果是时间点模式,验证是否能整除
|
|
|
if ($data['slot_mode'] == 2) {
|
|
|
$totalMinutes = ($endTime - $startTime) / 60;
|
|
|
if ($totalMinutes % $data['total_quota'] !== 0) {
|
|
|
return Rs::error('时间点模式下,时间段总时长必须能被号源数量整除');
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 验证科室和资源是否匹配
|
|
|
$resource = DB::table('department_resource')
|
|
|
->where('id', $data['resource_id'])
|
|
|
->where('department_id', $data['department_id'])
|
|
|
->first();
|
|
|
|
|
|
if (!$resource) {
|
|
|
return Rs::error('资源不属于该科室');
|
|
|
}
|
|
|
|
|
|
// 验证模板类型是否属于该科室
|
|
|
$modelType = DB::table('plan_model_type')
|
|
|
->where('id', $data['model_type_id'])
|
|
|
->first();
|
|
|
|
|
|
if (!$modelType) {
|
|
|
return Rs::error('模板类型不存在');
|
|
|
}
|
|
|
|
|
|
if ($modelType->department_id != $data['department_id']) {
|
|
|
return Rs::error('模板类型不属于该科室');
|
|
|
}
|
|
|
|
|
|
// 验证时间交集:同一模板分类下,同资源、相同星期的时间段不能有交集
|
|
|
$weeknames = explode(',', $data['weekname']);
|
|
|
$newStartTime = strtotime('2000-01-01 ' . $data['start_time']);
|
|
|
$newEndTime = strtotime('2000-01-01 ' . $data['end_time']);
|
|
|
|
|
|
foreach ($weeknames as $weekname) {
|
|
|
// 查询同一模板分类、同资源、同星期下已存在的模板(排除当前编辑的记录)
|
|
|
$query = DB::table('plan_model')
|
|
|
->where('model_type_id', $data['model_type_id'])
|
|
|
->where('resource_id', $data['resource_id'])
|
|
|
->where('weekname', $weekname)
|
|
|
->where('deleted', 0);
|
|
|
|
|
|
// 如果是编辑操作,排除当前记录
|
|
|
if ($data['id'] > 0) {
|
|
|
$query->where('id', '!=', $data['id']);
|
|
|
}
|
|
|
|
|
|
$existingModels = $query->get();
|
|
|
|
|
|
foreach ($existingModels as $existingModel) {
|
|
|
$existingStartTime = strtotime('2000-01-01 ' . $existingModel->start_time);
|
|
|
$existingEndTime = strtotime('2000-01-01 ' . $existingModel->end_time);
|
|
|
|
|
|
// 判断时间是否有交集:新时间段与已存在时间段重叠
|
|
|
if (!($newEndTime <= $existingStartTime || $newStartTime >= $existingEndTime)) {
|
|
|
return Rs::error('该资源在周' . $weekname . '已存在冲突的时间段');
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 权限控制:非管理员只能操作自己科室的模板
|
|
|
if (!$isAdmin) {
|
|
|
if (empty($userDeptId)) {
|
|
|
return Rs::error('请先绑定科室');
|
|
|
}
|
|
|
if ($data['department_id'] != $userDeptId) {
|
|
|
return Rs::error('无权限操作其他科室的数据');
|
|
|
}
|
|
|
|
|
|
// 如果是更新操作,检查模板是否属于该科室
|
|
|
if ($data['id'] > 0) {
|
|
|
$model = DB::table('plan_model')
|
|
|
->where('id', $data['id'])
|
|
|
->first();
|
|
|
|
|
|
if (!$model) {
|
|
|
return Rs::error('模板不存在');
|
|
|
}
|
|
|
|
|
|
if ($model->department_id != $userDeptId) {
|
|
|
return Rs::error('无权限操作该模板');
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
$conditions = ['id' => $data['id']];
|
|
|
$values = [
|
|
|
'model_type_id' => $data['model_type_id'],
|
|
|
'department_id' => $data['department_id'],
|
|
|
'resource_id' => $data['resource_id'],
|
|
|
'slot_mode' => $data['slot_mode'] ?? null,
|
|
|
'total_quota' => $data['total_quota'],
|
|
|
'weekname' => $data['weekname'],
|
|
|
'patient_type_mask' => $data['patient_type_mask'],
|
|
|
'start_time' => $data['start_time'],
|
|
|
'end_time' => $data['end_time'],
|
|
|
'cutoff_time' => $data['cutoff_time'],
|
|
|
'status' => $data['status'],
|
|
|
];
|
|
|
|
|
|
$channelQuotas = $data['channel_quotas'] ?? [];
|
|
|
|
|
|
// 校验渠道配额之和等于 total_quota
|
|
|
if (!empty($channelQuotas)) {
|
|
|
$channelTotal = 0;
|
|
|
foreach ($channelQuotas as $quota) {
|
|
|
if (isset($quota['total_quota'])) {
|
|
|
$channelTotal += $quota['total_quota'];
|
|
|
}
|
|
|
}
|
|
|
if ($channelTotal != $data['total_quota']) {
|
|
|
return Rs::error('渠道配额之和(' . $channelTotal . ')与号源总数(' . $data['total_quota'] . ')不相等,请检查后重试');
|
|
|
}
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
DB::beginTransaction();
|
|
|
|
|
|
// 解析星期列表
|
|
|
$weeknames = explode(',', $data['weekname']);
|
|
|
|
|
|
if ($data['id'] == 0) {
|
|
|
// 新增操作:为每个星期创建一条记录
|
|
|
$modelIds = [];
|
|
|
foreach ($weeknames as $weekname) {
|
|
|
$values['weekname'] = $weekname;
|
|
|
$values['created_at'] = now();
|
|
|
$values['updated_at'] = now();
|
|
|
$modelId = DB::table('plan_model')->insertGetId($values);
|
|
|
$modelIds[] = $modelId;
|
|
|
|
|
|
// 插入渠道配额
|
|
|
if (!empty($channelQuotas)) {
|
|
|
foreach ($channelQuotas as $quota) {
|
|
|
if (isset($quota['channel_id']) && isset($quota['total_quota']) && $quota['total_quota'] > 0) {
|
|
|
DB::table('plan_model_channel_quota')->insert([
|
|
|
'model_id' => $modelId,
|
|
|
'channel_id' => $quota['channel_id'],
|
|
|
'total_quota' => $quota['total_quota'],
|
|
|
'allow_overuse_public_pool' => $quota['allow_overuse_public_pool'] ?? 0,
|
|
|
'deleted' => 0,
|
|
|
'created_at' => now(),
|
|
|
'updated_at' => now(),
|
|
|
]);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
DB::commit();
|
|
|
// 返回第一个modelId
|
|
|
return Rs::success(['id' => $modelIds[0] ?? 0, 'model_ids' => $modelIds]);
|
|
|
} else {
|
|
|
// 更新操作
|
|
|
$values['updated_at'] = now();
|
|
|
$updated = DB::table('plan_model')
|
|
|
->where('id', $data['id'])
|
|
|
->update($values);
|
|
|
if ($updated === false) {
|
|
|
DB::rollBack();
|
|
|
return Rs::error('操作失败:模板不存在');
|
|
|
}
|
|
|
$modelId = $data['id'];
|
|
|
|
|
|
// 删除旧的渠道配额
|
|
|
DB::table('plan_model_channel_quota')
|
|
|
->where('model_id', $modelId)
|
|
|
->update(['deleted' => 1]);
|
|
|
|
|
|
// 插入新的渠道配额
|
|
|
if (!empty($channelQuotas)) {
|
|
|
foreach ($channelQuotas as $quota) {
|
|
|
if (isset($quota['channel_id']) && isset($quota['total_quota']) && $quota['total_quota'] > 0) {
|
|
|
DB::table('plan_model_channel_quota')->insert([
|
|
|
'model_id' => $modelId,
|
|
|
'channel_id' => $quota['channel_id'],
|
|
|
'total_quota' => $quota['total_quota'],
|
|
|
'allow_overuse_public_pool' => $quota['allow_overuse_public_pool'] ?? 0,
|
|
|
'deleted' => 0,
|
|
|
'created_at' => now(),
|
|
|
'updated_at' => now(),
|
|
|
]);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
DB::commit();
|
|
|
return Rs::success(['id' => $modelId]);
|
|
|
}
|
|
|
} catch (\Exception $e) {
|
|
|
DB::rollBack();
|
|
|
return Rs::error('保存失败:' . $e->getMessage());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 删除号源模板
|
|
|
* @param Request $request
|
|
|
* @return \Illuminate\Http\JsonResponse
|
|
|
*/
|
|
|
public function Delete(Request $request)
|
|
|
{
|
|
|
$payload = $request->attributes->get('payload');
|
|
|
if (!$payload) {
|
|
|
return Rs::error('用户信息无效', 401);
|
|
|
}
|
|
|
|
|
|
$user = $this->userService->getUserInfoFromPayload($payload);
|
|
|
if (!$user) {
|
|
|
return Rs::error('用户不存在', 401);
|
|
|
}
|
|
|
|
|
|
$isAdmin = $this->userService->isAdmin($payload);
|
|
|
$userDeptId = $user->dept_id ?? null;
|
|
|
|
|
|
$data = $request->validate([
|
|
|
'id' => 'required|integer',
|
|
|
]);
|
|
|
|
|
|
// 查询模板信息
|
|
|
$model = DB::table('plan_model')
|
|
|
->where('id', $data['id'])
|
|
|
->first();
|
|
|
|
|
|
if (!$model) {
|
|
|
return Rs::error('模板不存在');
|
|
|
}
|
|
|
|
|
|
// 权限控制:非管理员只能删除自己科室的模板
|
|
|
if (!$isAdmin) {
|
|
|
if (empty($userDeptId)) {
|
|
|
return Rs::error('请先绑定科室');
|
|
|
}
|
|
|
if ($model->department_id != $userDeptId) {
|
|
|
return Rs::error('无权限删除该模板');
|
|
|
}
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
DB::beginTransaction();
|
|
|
|
|
|
// 获取要删除的模板信息
|
|
|
$model = DB::table('plan_model')
|
|
|
->where('id', $data['id'])
|
|
|
->first();
|
|
|
|
|
|
if (!$model) {
|
|
|
DB::rollBack();
|
|
|
return Rs::error('模板不存在');
|
|
|
}
|
|
|
|
|
|
// 软删除模板(只删除这一条记录)
|
|
|
DB::table('plan_model')
|
|
|
->where('id', $data['id'])
|
|
|
->update(['deleted' => 1, 'updated_at' => now()]);
|
|
|
|
|
|
// 删除对应的渠道配额
|
|
|
DB::table('plan_model_channel_quota')
|
|
|
->where('model_id', $data['id'])
|
|
|
->update(['deleted' => 1]);
|
|
|
|
|
|
DB::commit();
|
|
|
return Rs::success();
|
|
|
} catch (\Exception $e) {
|
|
|
DB::rollBack();
|
|
|
return Rs::error('删除失败:' . $e->getMessage());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 生成号源
|
|
|
* @param Request $request
|
|
|
* @return \Illuminate\Http\JsonResponse
|
|
|
*/
|
|
|
public function generatePlanBatch(Request $request)
|
|
|
{
|
|
|
$payload = $request->attributes->get('payload');
|
|
|
if (!$payload) {
|
|
|
return Rs::error('用户信息无效', 401);
|
|
|
}
|
|
|
|
|
|
$user = $this->userService->getUserInfoFromPayload($payload);
|
|
|
if (!$user) {
|
|
|
return Rs::error('用户不存在', 401);
|
|
|
}
|
|
|
|
|
|
$isAdmin = $this->userService->isAdmin($payload);
|
|
|
$userDeptId = $user->dept_id ?? null;
|
|
|
|
|
|
$data = $request->validate([
|
|
|
'start_date' => 'required|date',
|
|
|
'end_date' => 'required|date|after_or_equal:start_date',
|
|
|
'models' => 'required|array|min:1',
|
|
|
'models.*.model_id' => 'required|integer',
|
|
|
'models.*.start_time' => 'required|date_format:H:i:s',
|
|
|
'models.*.end_time' => 'required|date_format:H:i:s',
|
|
|
'models.*.weekname' => 'required|string',
|
|
|
]);
|
|
|
|
|
|
try {
|
|
|
DB::beginTransaction();
|
|
|
|
|
|
$startDate = $data['start_date'];
|
|
|
$endDate = $data['end_date'];
|
|
|
$models = $data['models'];
|
|
|
|
|
|
// ========== 1. 预加载节假日数据 ==========
|
|
|
$holidayMap = [];
|
|
|
$holidayRecords = DB::table('holiday')
|
|
|
->where('deleted', 0)
|
|
|
->where('status', 1)
|
|
|
->whereRaw('date BETWEEN ? AND ?', [$startDate, $endDate])
|
|
|
->get();
|
|
|
foreach ($holidayRecords as $h) {
|
|
|
$holidayMap[$h->date] = $h->type;
|
|
|
}
|
|
|
|
|
|
// ========== 2. 预加载所有模板、模板类型、渠道配额 ==========
|
|
|
$modelIds = array_column($models, 'model_id');
|
|
|
$modelRecords = DB::table('plan_model')
|
|
|
->whereIn('id', $modelIds)
|
|
|
->where('deleted', 0)
|
|
|
->get()
|
|
|
->keyBy('id');
|
|
|
|
|
|
$modelTypeIds = $modelRecords->pluck('model_type_id')->unique()->toArray();
|
|
|
$modelTypeRecords = DB::table('plan_model_type')
|
|
|
->whereIn('id', $modelTypeIds)
|
|
|
->where('deleted', 0)
|
|
|
->get()
|
|
|
->keyBy('id');
|
|
|
|
|
|
$channelQuotaMap = [];
|
|
|
$channelQuotaRecords = DB::table('plan_model_channel_quota')
|
|
|
->whereIn('model_id', $modelIds)
|
|
|
->where('deleted', 0)
|
|
|
->select('id', 'model_id', 'channel_id', 'total_quota', 'allow_overuse_public_pool')
|
|
|
->get();
|
|
|
foreach ($channelQuotaRecords as $cq) {
|
|
|
$channelQuotaMap[$cq->model_id][] = $cq;
|
|
|
}
|
|
|
|
|
|
// ========== 3. 预校验:存在性、权限、状态、时间点整除 ==========
|
|
|
foreach ($models as $modelInfo) {
|
|
|
$modelId = $modelInfo['model_id'];
|
|
|
$model = $modelRecords->get($modelId);
|
|
|
|
|
|
if (!$model) {
|
|
|
DB::rollBack();
|
|
|
return Rs::error('模板不存在:' . $modelId);
|
|
|
}
|
|
|
|
|
|
if ($model->status != 1) {
|
|
|
DB::rollBack();
|
|
|
return Rs::error('模板 ' . $modelId . ' 已停用,不能生成号源');
|
|
|
}
|
|
|
|
|
|
if (!$isAdmin) {
|
|
|
if (empty($userDeptId)) {
|
|
|
DB::rollBack();
|
|
|
return Rs::error('请先绑定科室');
|
|
|
}
|
|
|
if ($model->department_id != $userDeptId) {
|
|
|
DB::rollBack();
|
|
|
return Rs::error('无权限操作该模板');
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if ($model->slot_mode == 2) {
|
|
|
$modelStartTime = strtotime('2000-01-01 ' . $model->start_time);
|
|
|
$modelEndTime = strtotime('2000-01-01 ' . $model->end_time);
|
|
|
$totalMinutes = ($modelEndTime - $modelStartTime) / 60;
|
|
|
if ($totalMinutes % $model->total_quota !== 0) {
|
|
|
DB::rollBack();
|
|
|
return Rs::error('时间点模式下,时间段总时长必须能被号源数量整除。当前模板时长 ' . $totalMinutes . ' 分钟,号源数量 ' . $model->total_quota . ',无法整除,请修改模板后再试');
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// ========== 4. 预加载冲突检测所需的所有已有号源批次 ==========
|
|
|
$resourceIds = $modelRecords->pluck('resource_id')->unique()->toArray();
|
|
|
$existingBatches = DB::table('plan_batch')
|
|
|
->whereIn('resource_id', $resourceIds)
|
|
|
->where('plan_date', '>=', $startDate)
|
|
|
->where('plan_date', '<=', $endDate)
|
|
|
->where('deleted', 0)
|
|
|
->select('id', 'resource_id', 'plan_date', 'start_time', 'end_time')
|
|
|
->get();
|
|
|
|
|
|
$existingByResourceAndDate = [];
|
|
|
foreach ($existingBatches as $eb) {
|
|
|
$key = $eb->resource_id . '_' . $eb->plan_date;
|
|
|
$existingByResourceAndDate[$key][] = $eb;
|
|
|
}
|
|
|
|
|
|
// ========== 5. 冲突检测 ==========
|
|
|
$allConflicts = [];
|
|
|
foreach ($models as $modelInfo) {
|
|
|
$model = $modelRecords->get($modelInfo['model_id']);
|
|
|
$modelType = $modelTypeRecords->get($model->model_type_id);
|
|
|
$allowedDateRanges = $this->resolveDateRanges($modelType);
|
|
|
$holidayMode = $modelType->holiday_mode ?? 0;
|
|
|
|
|
|
$currentDate = new \DateTime($startDate);
|
|
|
$endDateTime = new \DateTime($endDate);
|
|
|
|
|
|
while ($currentDate <= $endDateTime) {
|
|
|
$planDate = $currentDate->format('Y-m-d');
|
|
|
$shouldGenerate = $this->shouldGenerateForDate($currentDate, $model, $allowedDateRanges, $holidayMode, $holidayMap);
|
|
|
|
|
|
if ($shouldGenerate) {
|
|
|
$key = $model->resource_id . '_' . $planDate;
|
|
|
$existing = $existingByResourceAndDate[$key] ?? [];
|
|
|
$newStartTime = strtotime('2000-01-01 ' . $model->start_time);
|
|
|
$newEndTime = strtotime('2000-01-01 ' . $model->end_time);
|
|
|
|
|
|
foreach ($existing as $plan) {
|
|
|
$existingStartTime = strtotime('2000-01-01 ' . $plan->start_time);
|
|
|
$existingEndTime = strtotime('2000-01-01 ' . $plan->end_time);
|
|
|
if (!($newEndTime <= $existingStartTime || $newStartTime >= $existingEndTime)) {
|
|
|
$allConflicts[] = $planDate . ' ' . $model->start_time . '-' . $model->end_time;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
$currentDate->modify('+1 day');
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (!empty($allConflicts)) {
|
|
|
$uniqueConflicts = array_unique($allConflicts);
|
|
|
sort($uniqueConflicts);
|
|
|
DB::rollBack();
|
|
|
return Rs::error('以下时间段已存在号源,无法生成:' . implode('、', $uniqueConflicts));
|
|
|
}
|
|
|
|
|
|
// ========== 6. 生成号源 ==========
|
|
|
$totalCount = 0;
|
|
|
$skippedDates = [];
|
|
|
|
|
|
foreach ($models as $modelInfo) {
|
|
|
$model = $modelRecords->get($modelInfo['model_id']);
|
|
|
$modelType = $modelTypeRecords->get($model->model_type_id);
|
|
|
$allowedDateRanges = $this->resolveDateRanges($modelType);
|
|
|
$hasDateLimit = !empty($modelType->date_info);
|
|
|
$holidayMode = $modelType->holiday_mode ?? 0;
|
|
|
$channelQuotas = collect($channelQuotaMap[$model->id] ?? []);
|
|
|
|
|
|
$currentDate = new \DateTime($startDate);
|
|
|
$endDateTime = new \DateTime($endDate);
|
|
|
|
|
|
while ($currentDate <= $endDateTime) {
|
|
|
$planDate = $currentDate->format('Y-m-d');
|
|
|
$shouldGenerate = $this->shouldGenerateForDate($currentDate, $model, $allowedDateRanges, $holidayMode, $holidayMap);
|
|
|
|
|
|
if ($shouldGenerate) {
|
|
|
$hasChannelQuota = $channelQuotas->where('channel_id', '>', 0)->isNotEmpty();
|
|
|
|
|
|
$batchId = DB::table('plan_batch')->insertGetId([
|
|
|
'model_id' => $model->id,
|
|
|
'slot_mode' => $model->slot_mode,
|
|
|
'plan_date' => $planDate,
|
|
|
'weekname' => $currentDate->format('N'),
|
|
|
'department_id' => $model->department_id,
|
|
|
'resource_id' => $model->resource_id,
|
|
|
'patient_type_mask' => $model->patient_type_mask,
|
|
|
'total_quota' => $model->total_quota,
|
|
|
'used_quota' => 0,
|
|
|
'start_time' => $model->start_time,
|
|
|
'end_time' => $model->end_time,
|
|
|
'cutoff_time' => $model->cutoff_time,
|
|
|
'status' => $model->status,
|
|
|
'enable_channel_quota' => $hasChannelQuota ? 1 : 0,
|
|
|
'adduser' => $user->id,
|
|
|
'deleted' => 0,
|
|
|
'created_at' => now(),
|
|
|
'updated_at' => now(),
|
|
|
]);
|
|
|
|
|
|
if ($model->slot_mode == 1) {
|
|
|
DB::table('plan')->insert([
|
|
|
'batch_id' => $batchId,
|
|
|
'start_time' => $model->start_time,
|
|
|
'end_time' => $model->end_time,
|
|
|
'total_quota' => $model->total_quota,
|
|
|
'used_quota' => 0,
|
|
|
'status' => $model->status,
|
|
|
'created_at' => now(),
|
|
|
'updated_at' => now(),
|
|
|
]);
|
|
|
$totalCount++;
|
|
|
} else {
|
|
|
$modelStartTime = strtotime('2000-01-01 ' . $model->start_time);
|
|
|
$modelEndTime = strtotime('2000-01-01 ' . $model->end_time);
|
|
|
$totalMinutes = ($modelEndTime - $modelStartTime) / 60;
|
|
|
$intervalMinutes = $totalMinutes / $model->total_quota;
|
|
|
|
|
|
for ($i = 0; $i < $model->total_quota; $i++) {
|
|
|
$pointStartTime = $modelStartTime + ($i * $intervalMinutes * 60);
|
|
|
$pointEndTime = $pointStartTime + ($intervalMinutes * 60);
|
|
|
|
|
|
DB::table('plan')->insert([
|
|
|
'batch_id' => $batchId,
|
|
|
'start_time' => date('H:i:s', $pointStartTime),
|
|
|
'end_time' => date('H:i:s', $pointEndTime),
|
|
|
'total_quota' => 1,
|
|
|
'used_quota' => 0,
|
|
|
'status' => $model->status,
|
|
|
'created_at' => now(),
|
|
|
'updated_at' => now(),
|
|
|
]);
|
|
|
$totalCount++;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
foreach ($channelQuotas as $quota) {
|
|
|
DB::table('plan_batch_channel_quota')->insert([
|
|
|
'batch_id' => $batchId,
|
|
|
'channel_id' => $quota->channel_id,
|
|
|
'total_quota' => $quota->total_quota,
|
|
|
'used_quota' => 0,
|
|
|
'created_at' => now(),
|
|
|
'updated_at' => now(),
|
|
|
]);
|
|
|
}
|
|
|
} else {
|
|
|
$skipReason = null;
|
|
|
$planMonthDay = $currentDate->format('m-d');
|
|
|
if ($hasDateLimit && !empty($allowedDateRanges)) {
|
|
|
$dateAllowed = false;
|
|
|
foreach ($allowedDateRanges as $range) {
|
|
|
if ($planMonthDay >= $range[0] && $planMonthDay <= $range[1]) {
|
|
|
$dateAllowed = true;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
if (!$dateAllowed) {
|
|
|
$skipReason = '不在模板类型允许的日期范围内';
|
|
|
}
|
|
|
}
|
|
|
if (!$skipReason && !in_array($currentDate->format('N'), explode(',', $model->weekname))) {
|
|
|
$skipReason = '星期不匹配';
|
|
|
}
|
|
|
if ($skipReason) {
|
|
|
$skippedDates[$planDate] = $skipReason;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
$currentDate->modify('+1 day');
|
|
|
}
|
|
|
}
|
|
|
|
|
|
DB::commit();
|
|
|
|
|
|
$resultData = [
|
|
|
'count' => $totalCount,
|
|
|
'skipped_dates' => $skippedDates
|
|
|
];
|
|
|
|
|
|
$message = '成功生成 ' . $totalCount . ' 个号源';
|
|
|
if (!empty($skippedDates)) {
|
|
|
$skippedCount = count($skippedDates);
|
|
|
$message .= ",跳过 {$skippedCount} 个日期(不符合模板类型的日期范围限制)";
|
|
|
}
|
|
|
|
|
|
return Rs::success($resultData, $message);
|
|
|
} catch (\Exception $e) {
|
|
|
DB::rollBack();
|
|
|
return Rs::error('生成号源失败:' . $e->getMessage());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private function resolveDateRanges($modelType): array
|
|
|
{
|
|
|
$allowedDateRanges = [];
|
|
|
if ($modelType && !empty($modelType->date_info)) {
|
|
|
$dateInfo = json_decode($modelType->date_info, true);
|
|
|
if ($modelType->type == 1) {
|
|
|
if (is_array($dateInfo) && count($dateInfo) > 0) {
|
|
|
if (is_array($dateInfo[0]) && count($dateInfo[0]) == 2) {
|
|
|
$allowedDateRanges = $dateInfo;
|
|
|
} else {
|
|
|
$allowedDateRanges = [$dateInfo];
|
|
|
}
|
|
|
}
|
|
|
} else {
|
|
|
if (is_array($dateInfo) && count($dateInfo) > 0) {
|
|
|
foreach ($dateInfo as $date) {
|
|
|
$allowedDateRanges[] = [$date, $date];
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return $allowedDateRanges;
|
|
|
}
|
|
|
|
|
|
private function shouldGenerateForDate(\DateTime $currentDate, $model, array $allowedDateRanges, int $holidayMode, array $holidayMap): bool
|
|
|
{
|
|
|
$planDate = $currentDate->format('Y-m-d');
|
|
|
$planMonthDay = $currentDate->format('m-d');
|
|
|
$dayOfWeek = $currentDate->format('N');
|
|
|
|
|
|
$dateAllowed = false;
|
|
|
if (empty($allowedDateRanges)) {
|
|
|
$dateAllowed = true;
|
|
|
} else {
|
|
|
foreach ($allowedDateRanges as $range) {
|
|
|
if ($planMonthDay >= $range[0] && $planMonthDay <= $range[1]) {
|
|
|
$dateAllowed = true;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
$isHoliday = isset($holidayMap[$planDate]) && $holidayMap[$planDate] == 1;
|
|
|
$isMakeupWork = isset($holidayMap[$planDate]) && $holidayMap[$planDate] == 2;
|
|
|
$weeknameMatch = in_array($dayOfWeek, explode(',', $model->weekname));
|
|
|
|
|
|
if ($holidayMode == 1) {
|
|
|
return $dateAllowed && $weeknameMatch;
|
|
|
} elseif ($holidayMode == 2) {
|
|
|
return $dateAllowed && $isHoliday;
|
|
|
} else {
|
|
|
if ($isHoliday) {
|
|
|
return false;
|
|
|
} elseif ($isMakeupWork) {
|
|
|
return $dateAllowed;
|
|
|
} else {
|
|
|
return $dateAllowed && $weeknameMatch;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|