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.

687 lines
28 KiB
PHP

<?php
namespace App\Http\Controllers\API\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use App\Lib\Rs;
use App\Services\AppointmentService;
class AppointmentController extends Controller
{
private AppointmentService $appointmentService;
private \App\Services\PlanService $planService;
private \App\Services\BlockService $blockService;
public function __construct(AppointmentService $appointmentService, \App\Services\PlanService $planService, \App\Services\BlockService $blockService)
{
$this->appointmentService = $appointmentService;
$this->planService = $planService;
$this->blockService = $blockService;
}
/**
* 创建预约
*
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function create(Request $request)
{
$user = $request->attributes->get('payload');
if (!$user) {
return Rs::error('用户信息获取失败');
}
// 获取用户ID
$userId = $user['user_id'] ?? null;
if(empty($userId)){
return Rs::error('用户信息获取失败');
}
$applicationIds = $request->input('application_ids');
$channelId = $request->input('channel_id');
$planId = $request->input('plan_id');
// 验证参数
if (empty($applicationIds) || !is_array($applicationIds)) {
return Rs::error('请提供医嘱ID数组');
}
if (empty($channelId)) {
return Rs::error('请提供渠道ID');
}
if (empty($planId)) {
return Rs::error('请提供号源ID');
}
$result = $this->appointmentService->createAppointment(
$userId,
$applicationIds,
(int)$channelId,
(int)$planId
);
if ($result['success']) {
return Rs::success($result['data'] ?? null, $result['message']);
} else {
$data = $result['data'] ?? [];
$data['conflicts'] = $result['conflicts'] ?? [];
return Rs::error($result['message'], 400, $data);
}
}
/**
* 取消预约
*
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function cancel(Request $request)
{
$user = $request->attributes->get('payload');
if (!$user) {
return Rs::error('用户信息获取失败');
}
// 获取用户ID
$userId = $user['user_id'] ?? null;
if(empty($userId)){
return Rs::error('用户信息获取失败');
}
$appointmentId = $request->input('appointment_id');
$cancelReason = $request->input('cancel_reason');
// 验证参数
if (empty($appointmentId)) {
return Rs::error('请提供预约ID');
}
$result = $this->appointmentService->cancelAppointment($userId,(int)$appointmentId, $cancelReason);
if ($result['success']) {
return Rs::success(null, $result['message']);
} else {
return Rs::error($result['message']);
}
}
/**
* 改约
*
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function reschedule(Request $request)
{
$user = $request->attributes->get('payload');
if (!$user) {
return Rs::error('用户信息获取失败');
}
// 获取用户ID
$userId = $user['user_id'] ?? null;
if(empty($userId)){
return Rs::error('用户信息获取失败');
}
$appointmentId = $request->input('appointment_id');
$cancelReason = $request->input('cancel_reason');
$newPlanId = $request->input('plan_id');
// 验证参数
if (empty($appointmentId)) {
return Rs::error('请提供原预约ID');
}
if (empty($newPlanId)) {
return Rs::error('请提供新号源ID');
}
if (empty($cancelReason)) {
return Rs::error('请提供改约原因');
}
$result = $this->appointmentService->rescheduleAppointment(
$userId,
(int)$appointmentId,
$cancelReason,
(int)$newPlanId
);
if ($result['success']) {
return Rs::success($result['data'] ?? null, $result['message']);
} else {
$data = $result['data'] ?? [];
$data['conflicts'] = $result['conflicts'] ?? [];
return Rs::error($result['message'], 400, $data);
}
}
/**
* 自动预约接口
*
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function autoCreate(Request $request)
{
$user = $request->attributes->get('payload');
if (!$user) {
return Rs::error('用户信息获取失败');
}
$userId = $user['user_id'] ?? null;
if (empty($userId)) {
return Rs::error('用户信息获取失败');
}
// 验证参数
$data = $request->validate([
'channel_id' => 'required|integer|min:1',
'patient_info' => 'required|array',
'patient_info.*.patient_id' => 'required|integer',
'patient_info.*.his_order_ids' => 'required|array',
'patient_info.*.his_order_ids.*' => 'string',
'date_range' => 'nullable|array',
'date_range.*' => 'date'
]);
$channelId = $data['channel_id'];
$patientInfo = $data['patient_info'];
$dateRange = $data['date_range'] ?? null;
// 确定日期范围
if (empty($dateRange)) {
$startDate = date('Y-m-d');
$days = 15;
} else {
$startDate = $dateRange[0];
$endDate = $dateRange[1];
$days = (strtotime($endDate) - strtotime($startDate)) / 86400 + 1;
if ($days < 1) {
return Rs::error('日期范围无效');
}
}
// 初始化结果
$results = [];
$totalApplications = 0;
$totalSuccessful = 0;
$totalFailed = 0;
// 按患者遍历
foreach ($patientInfo as $patientData) {
$patientId = $patientData['patient_id'];
$hisOrderIds = $patientData['his_order_ids'];
// 查询该患者的医嘱(患者信息包含在 exam_application 表中)
$applications = DB::table('exam_application')
->where('patient_id', $patientId)
->whereIn('his_order_id', $hisOrderIds)
->where('status', 0) // 申请中状态
->get();
// 黑名单检测
$patientIdCardNo = $applications->first()->patient_id_card_no ?? null;
if ($patientIdCardNo) {
$blockCheck = $this->blockService->checkAppointmentPermission($channelId, $patientIdCardNo);
if (!$blockCheck['status']) {
// 黑名单检测失败,记录失败
$patientResult = [
'patient_id' => $patientId,
'patient_name' => $applications->first()->patient_name ?? '未知',
'appointments' => [],
'failed_applications' => [],
'summary' => [
'total_count' => count($hisOrderIds),
'success_count' => 0,
'failed_count' => count($hisOrderIds)
]
];
foreach ($hisOrderIds as $hisOrderId) {
$patientResult['failed_applications'][] = [
'his_order_id' => $hisOrderId,
'error_reason' => $blockCheck['msg']
];
}
$totalFailed += count($hisOrderIds);
$results[] = $patientResult;
continue;
}
}
// 从医嘱中获取患者姓名
$patientName = $applications->first()->patient_name ?? '未知';
$examApplicationIds = $applications->pluck('id')->toArray();
$totalApplications += count($hisOrderIds);
// 初始化该患者的预约结果
$patientResult = [
'patient_id' => $patientId,
'patient_name' => $patientName,
'appointments' => [],
'failed_applications' => [],
'summary' => [
'total_count' => count($hisOrderIds),
'success_count' => 0,
'failed_count' => 0
]
];
// 检查医嘱是否存在
if (empty($examApplicationIds)) {
foreach ($hisOrderIds as $hisOrderId) {
$patientResult['failed_applications'][] = [
'his_order_id' => $hisOrderId,
'error_reason' => '医嘱不存在或状态不符合预约条件'
];
}
$patientResult['summary']['failed_count'] = count($hisOrderIds);
$totalFailed += count($hisOrderIds);
$results[] = $patientResult;
continue;
}
// 每个患者的预约放在一个事务里
try {
DB::transaction(function () use (
$userId,
$channelId,
$startDate,
$days,
$applications,
$examApplicationIds,
$hisOrderIds,
&$patientResult,
&$totalSuccessful,
&$totalFailed
) {
// 尝试为所有医嘱查找共同资源的最早号源
$commonSlot = $this->planService->findEarliestAvailableSlot(
$examApplicationIds,
$channelId,
$startDate,
$days
);
if ($commonSlot) {
// 有共同资源且配额充足,统一预约
$result = $this->appointmentService->createAppointment(
$userId,
$examApplicationIds,
$channelId,
$commonSlot['plan_id']
);
if ($result['success']) {
// 获取预约记录
$appointmentIds = $result['data']['appointment_ids'] ?? [];
$appointments = DB::table('exam_appointment')
->whereIn('exam_appointment.id', $appointmentIds)
->leftJoin('exam_application', 'exam_appointment.exam_application_id', '=', 'exam_application.id')
->leftJoin('department_resource', 'exam_appointment.department_resource_id', '=', 'department_resource.id')
->select(
'exam_appointment.id as appointment_id',
'exam_application.his_order_id',
'exam_application.examination_item_name',
'department_resource.name as resource_name',
'exam_appointment.appointment_time'
)
->get();
foreach ($appointments as $appointment) {
$patientResult['appointments'][] = [
'appointment_id' => $appointment->appointment_id,
'his_order_id' => $appointment->his_order_id,
'examination_item_name' => $appointment->examination_item_name,
'resource_name' => $appointment->resource_name,
'appointment_time' => $appointment->appointment_time,
'time_slot_id' => $commonSlot['plan_id']
];
}
$patientResult['summary']['success_count'] = count($appointmentIds);
$totalSuccessful += count($appointmentIds);
} else {
// 预约失败,所有医嘱都记录为失败
foreach ($applications as $app) {
$patientResult['failed_applications'][] = [
'his_order_id' => $app->his_order_id,
'examination_item_name' => $app->examination_item_name,
'error_reason' => $result['message']
];
}
$patientResult['summary']['failed_count'] = count($applications);
$totalFailed += count($applications);
}
} else {
// 没有共同资源或配额不足,分别预约每个医嘱
foreach ($applications as $app) {
$singleSlot = $this->planService->findEarliestAvailableSlot(
[$app->id],
$channelId,
$startDate,
$days
);
if ($singleSlot) {
$result = $this->appointmentService->createAppointment(
$userId,
[$app->id],
$channelId,
$singleSlot['plan_id']
);
if ($result['success']) {
$appointmentId = $result['data']['appointment_ids'][0] ?? null;
if ($appointmentId) {
$appointment = DB::table('exam_appointment')
->where('exam_appointment.id', $appointmentId)
->leftJoin('exam_application', 'exam_appointment.exam_application_id', '=', 'exam_application.id')
->leftJoin('department_resource', 'exam_appointment.department_resource_id', '=', 'department_resource.id')
->select(
'exam_appointment.id as appointment_id',
'exam_application.his_order_id',
'exam_application.examination_item_name',
'department_resource.name as resource_name',
'exam_appointment.appointment_time'
)
->first();
if ($appointment) {
$patientResult['appointments'][] = [
'appointment_id' => $appointment->appointment_id,
'his_order_id' => $appointment->his_order_id,
'examination_item_name' => $appointment->examination_item_name,
'resource_name' => $appointment->resource_name,
'appointment_time' => $appointment->appointment_time,
'time_slot_id' => $singleSlot['plan_id']
];
}
}
$patientResult['summary']['success_count']++;
$totalSuccessful++;
} else {
$patientResult['failed_applications'][] = [
'his_order_id' => $app->his_order_id,
'examination_item_name' => $app->examination_item_name,
'error_reason' => $result['message']
];
$patientResult['summary']['failed_count']++;
$totalFailed++;
}
} else {
$patientResult['failed_applications'][] = [
'his_order_id' => $app->his_order_id,
'examination_item_name' => $app->examination_item_name,
'error_reason' => '指定日期范围内无可用号源'
];
$patientResult['summary']['failed_count']++;
$totalFailed++;
}
}
}
});
} catch (\Exception $e) {
// 事务失败,记录错误
foreach ($applications as $app) {
$patientResult['failed_applications'][] = [
'his_order_id' => $app->his_order_id,
'examination_item_name' => $app->examination_item_name,
'error_reason' => '预约失败:' . $e->getMessage()
];
}
$patientResult['summary']['failed_count'] = count($applications);
$totalFailed += count($applications);
}
$results[] = $patientResult;
}
// 返回结果
$allSuccess = $totalFailed === 0;
return Rs::success([
'results' => $results,
'total_summary' => [
'total_applications' => $totalApplications,
'successful_appointments' => $totalSuccessful,
'failed_applications' => $totalFailed,
'patients_count' => count($patientInfo)
]
], $allSuccess ? '自动预约成功' : '部分医嘱预约成功');
}
/**
* 预约预览接口(模拟预约,不实际创建)
*
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function preview(Request $request)
{
$user = $request->attributes->get('payload');
if (!$user) {
return Rs::error('用户信息获取失败');
}
// 验证参数
$data = $request->validate([
'channel_id' => 'required|integer|min:1',
'patient_info' => 'required|array',
'patient_info.*.patient_id' => 'required|integer',
'patient_info.*.his_order_ids' => 'required|array',
'patient_info.*.his_order_ids.*' => 'string',
'date_range' => 'nullable|array',
'date_range.*' => 'date'
]);
$channelId = $data['channel_id'];
$patientInfo = $data['patient_info'];
$dateRange = $data['date_range'] ?? null;
// 确定日期范围
if (empty($dateRange)) {
$startDate = date('Y-m-d');
$days = 15;
} else {
$startDate = $dateRange[0];
$endDate = $dateRange[1];
$days = (strtotime($endDate) - strtotime($startDate)) / 86400 + 1;
if ($days < 1) {
return Rs::error('日期范围无效');
}
}
// 初始化结果
$results = [];
$totalApplications = 0;
$totalSuccessful = 0;
$totalFailed = 0;
// 按患者遍历
foreach ($patientInfo as $patientData) {
$patientId = $patientData['patient_id'];
$hisOrderIds = $patientData['his_order_ids'];
// 查询该患者的医嘱
$applications = DB::table('exam_application')
->where('patient_id', $patientId)
->whereIn('his_order_id', $hisOrderIds)
->where('status', 0)
->get();
// 黑名单检测
$patientIdCardNo = $applications->first()->patient_id_card_no ?? null;
if ($patientIdCardNo) {
$blockCheck = $this->blockService->checkAppointmentPermission($channelId, $patientIdCardNo);
if (!$blockCheck['status']) {
// 黑名单检测失败,记录失败
$patientResult = [
'patient_id' => $patientId,
'patient_name' => $applications->first()->patient_name ?? '未知',
'appointments' => [],
'failed_applications' => [],
'summary' => [
'total_count' => count($hisOrderIds),
'success_count' => 0,
'failed_count' => count($hisOrderIds)
]
];
foreach ($hisOrderIds as $hisOrderId) {
$patientResult['failed_applications'][] = [
'his_order_id' => $hisOrderId,
'error_reason' => $blockCheck['msg']
];
}
$totalFailed += count($hisOrderIds);
$results[] = $patientResult;
continue;
}
}
// 从医嘱中获取患者姓名
$patientName = $applications->first()->patient_name ?? '未知';
$examApplicationIds = $applications->pluck('id')->toArray();
$totalApplications += count($hisOrderIds);
// 初始化该患者的预览结果
$patientResult = [
'patient_id' => $patientId,
'patient_name' => $patientName,
'appointments' => [],
'failed_applications' => [],
'summary' => [
'total_count' => count($hisOrderIds),
'success_count' => 0,
'failed_count' => 0
]
];
// 检查医嘱是否存在
if (empty($examApplicationIds)) {
foreach ($hisOrderIds as $hisOrderId) {
$patientResult['failed_applications'][] = [
'his_order_id' => $hisOrderId,
'error_reason' => '医嘱不存在或状态不符合预约条件'
];
}
$patientResult['summary']['failed_count'] = count($hisOrderIds);
$totalFailed += count($hisOrderIds);
$results[] = $patientResult;
continue;
}
// 尝试为所有医嘱查找共同资源的最早号源(不创建预约)
$commonSlot = $this->planService->findEarliestAvailableSlot(
$examApplicationIds,
$channelId,
$startDate,
$days
);
if ($commonSlot) {
// 有共同资源且配额充足,生成统一预览
// 查询相关信息用于预览
$planInfo = DB::table('plan')
->leftJoin('plan_batch', 'plan.batch_id', '=', 'plan_batch.id')
->where('plan.id', $commonSlot['plan_id'])
->select('plan.start_time', 'plan.end_time', 'plan_batch.plan_date')
->first();
$appointmentTime = null;
if ($planInfo && $planInfo->plan_date) {
$startTime = $planInfo->start_time ? substr($planInfo->start_time, 0, 5) : '';
$endTime = $planInfo->end_time ? substr($planInfo->end_time, 0, 5) : '';
$timeRange = ($startTime && $endTime) ? " {$startTime}-{$endTime}" : '';
$appointmentTime = $planInfo->plan_date . $timeRange;
}
$resource = DB::table('department_resource')
->where('id', $commonSlot['resource_id'])
->first();
$resourceName = $resource->name ?? '未知';
foreach ($applications as $app) {
$patientResult['appointments'][] = [
'appointment_id' => null, // 预览时不生成预约ID
'exam_application_id' => $app->id,
'his_order_id' => $app->his_order_id,
'examination_item_name' => $app->examination_item_name,
'resource_name' => $resourceName,
'appointment_time' => $appointmentTime,
'time_slot_id' => $commonSlot['plan_id']
];
}
$patientResult['summary']['success_count'] = count($applications);
$totalSuccessful += count($applications);
} else {
// 没有共同资源或配额不足,分别查找每个医嘱的号源
foreach ($applications as $app) {
$singleSlot = $this->planService->findEarliestAvailableSlot(
[$app->id],
$channelId,
$startDate,
$days
);
if ($singleSlot) {
// 查询相关信息用于预览
$planInfo = DB::table('plan')
->leftJoin('plan_batch', 'plan.batch_id', '=', 'plan_batch.id')
->where('plan.id', $singleSlot['plan_id'])
->select('plan.start_time', 'plan.end_time', 'plan_batch.plan_date')
->first();
$appointmentTime = null;
if ($planInfo && $planInfo->plan_date) {
$startTime = $planInfo->start_time ? substr($planInfo->start_time, 0, 5) : '';
$endTime = $planInfo->end_time ? substr($planInfo->end_time, 0, 5) : '';
$timeRange = ($startTime && $endTime) ? " {$startTime}-{$endTime}" : '';
$appointmentTime = $planInfo->plan_date . $timeRange;
}
$resource = DB::table('department_resource')
->where('id', $singleSlot['resource_id'])
->first();
$patientResult['appointments'][] = [
'appointment_id' => null,
'exam_application_id' => $app->id,
'his_order_id' => $app->his_order_id,
'examination_item_name' => $app->examination_item_name,
'resource_name' => $resource->name ?? '未知',
'appointment_time' => $appointmentTime,
'time_slot_id' => $singleSlot['plan_id']
];
$patientResult['summary']['success_count']++;
$totalSuccessful++;
} else {
$patientResult['failed_applications'][] = [
'his_order_id' => $app->his_order_id,
'examination_item_name' => $app->examination_item_name,
'error_reason' => '指定日期范围内无可用号源'
];
$patientResult['summary']['failed_count']++;
$totalFailed++;
}
}
}
$results[] = $patientResult;
}
// 返回预览结果
return Rs::success([
'results' => $results,
'total_summary' => [
'total_applications' => $totalApplications,
'successful_appointments' => $totalSuccessful,
'failed_applications' => $totalFailed,
'patients_count' => count($patientInfo)
]
], '预约预览生成成功');
}
}