加急预约

main
岩仔88 2 weeks ago
parent 22858a2b4f
commit e96b5e7fcd

@ -392,6 +392,7 @@ class PlanListController extends Controller
date_default_timezone_set('PRC');
$nowdatetime = date("Y-m-d H:i:s");
$do_userid=request('do_user');
$is_emergency = request('is_emergency');//是否加急
$planid = request('planid');
$appointment_type = request('appointment_type');//渠道id
$mainlistid = request('mainlistid');//主表id
@ -399,7 +400,7 @@ class PlanListController extends Controller
if (!isset($do_type)) return \Yz::echoError1('参数:操作类型 不能为空');
$service = new PlanListService();
return $service->YuYue($planid, $appointment_type, $mainlistid, $do_type,$do_userid);
return $service->YuYue($planid, $appointment_type, $mainlistid, $do_type,$is_emergency,$do_userid);
}
//自动预约
public function AutoYuYue()

@ -11,6 +11,7 @@ class GroupService
$sql=" where status =1";
}
$query=DB::select("select * from `group` ".$sql);
$result['special_privileges_list']=config('app.globals.特殊权限');
if(count($query)){
$result['list']=$query;
$result['status']='ok';

@ -38,6 +38,7 @@ class UserService
'cn_name' => $arr['info']['cname'],
'username' => $arr['info']['uname'],
'status' => $arr['info']['status'],
'special_privileges' => json_encode($arr['info']['special_privileges'], JSON_UNESCAPED_UNICODE),
'group_locked' => 1,
];
@ -91,7 +92,7 @@ class UserService
return $result;
}
public function GetDetail($arr){
$c=DB::table('users')->select(['id','cn_name','username','department_id','department_ids','status','group','img',])->where(['id'=>$arr['id']])->whereIn('status',[0,1])->get();
$c=DB::table('users')->select(['id','cn_name','username','department_id','department_ids','status','group','img','special_privileges'])->where(['id'=>$arr['id']])->whereIn('status',[0,1])->get();
if(count($c)){
$ids=[];
if(!empty($c[0]->department_ids)){
@ -114,6 +115,12 @@ class UserService
}
$c[0]->department_info=$departments_arr;
$privileges = json_decode($c[0]->special_privileges, true);
if ($privileges === null && json_last_error() !== JSON_ERROR_NONE) {
$c[0]->special_privileges = [];
} else {
$c[0]->special_privileges = $privileges;
}
$result['info']=$c;
$result['status']='ok';
$result['msg']='成功';

@ -17,6 +17,7 @@ class PlanListService
{
date_default_timezone_set('PRC');
$allEmergency = true;
$allDevice = [];//所有医嘱检查项目绑定的设备id
$commPatientType = [];//所有医嘱共同的病人类型
$zhanweiCount=0;//勾选的检查项目共计占多少名额
@ -24,6 +25,11 @@ class PlanListService
// $info = DB::table('s_list')->where(['reg_num' => $regnum, 'entrust_id' => $entrustid, 'episodeid' => $episodeid, 'is_nullify' => 0])->first();
$info = DB::table('s_list')->where(['entrust_id' => $entrustid, 'is_nullify' => 0])->first();
if (!$info) return \Yz::echoError1('没有找到对应医嘱信息:'.$entrustid);
if (!isset($info->his_is_emergency) || $info->his_is_emergency != 1) {
$allEmergency = false;
}
$itemInfo = DB::table('s_check_item')->where(['item_name' => $info->entrust, 'status' => 1, "is_del" => 0])->get();
if (count($itemInfo) == 0) return \Yz::echoError1('没有找到可用的检查项目信息');
$itemInfo = $itemInfo[0];
@ -179,22 +185,34 @@ WHERE
'mainInfo' => $info,
'plan_list' => $pp,
'zhanWeiCount'=>$zhanweiCount,
'earliestPlan'=>$earliestPlan
'earliestPlan'=>$earliestPlan,
'is_emergency' => $allEmergency,
]);
}
//开始预约占用名额
public function YuYue($planid, $appointment_type, $mainlistids, $do_type,$do_user=null)
public function YuYue($planid, $appointment_type, $mainlistids, $do_type,$is_emergency=0,$do_user=null)
{
date_default_timezone_set('PRC');
$nowdatetime = date("Y-m-d H:i:s");
// $planid = request('planid');
// $appointment_type = request('appointment_type');//渠道id
// $mainlistid = request('mainlistid');//主表id
// $do_type = request('dotype');//操作类型,1预约2改约
// if (!isset($do_type)) return \Yz::echoError1('参数:操作类型 不能为空');
if ($is_emergency == 1 && !empty($do_user)) {//如果是加急,校验用户是否有加急权限
$do_user_info=DB::table('users')->where('id',$do_user)->first();
if(!$do_user_info){
return \Yz::echoError1('无效的用户ID无法进行加急预约');
}else{
$special_privileges=[];
$decoded=json_decode($do_user_info->special_privileges,true);
if(is_array($decoded)){
$special_privileges=$decoded;
}
if(!in_array('emergency', $special_privileges)){
return \Yz::echoError1('无权进行加急预约');
}
}
}else{
$is_emergency=0;
}
$planInfo = DB::table('s_source_roster_detail')->where(['id' => $planid, 'status' => 1, 'is_del' => 0])->first();
if (!$planInfo) return \Yz::echoError1('当前时段不可用');
@ -334,7 +352,18 @@ WHERE
}
}
if ($planZongCount < ($planZongUsedCount + $planZongLockedCount + $zhanweiCount)) return \Yz::echoError1('当前预约时间名额不足');
if ($planZongCount < ($planZongUsedCount + $planZongLockedCount + $zhanweiCount)){
if($is_emergency!==1){
return \Yz::echoError1('当前预约时间名额不足');
}else{ //如果是紧急预约,则把未分配的 强制加到当前预约渠道
foreach ($roster_detail_counts as $key=> $roster_detail_count) {
if($roster_detail_count->appointment_type_id==$appointment_type){
$plan_qudao_tempCount[$key]=$weifenpeiCount;
}
}
}
}
//判断某人这些待预约项目里,是否存在互斥
@ -363,27 +392,37 @@ WHERE
try {
//更新计划明细表使用数量
$up_plan_count_all_success =true;
$appointment_use_plan_detail_arr=[];//预约各个号源占用号源详情
foreach ($roster_detail_counts as $key => $planCount) {
if($plan_qudao_tempCount[$key]==0) continue;
$u = DB::table('s_source_roster_detail_count')
->where(['id' => $planCount->id])
->whereRaw('count >= (used_count + IFNULL(locked_count, 0) + ?)', [$plan_qudao_tempCount[$key]])
->increment('used_count',$plan_qudao_tempCount[$key]);
$query = DB::table('s_source_roster_detail_count')->where(['id' => $planCount->id]);
// 【核心修改】只有非紧急预约时,才检查名额是否充足
if ($is_emergency!==1) {
$query->whereRaw('count >= (used_count + IFNULL(locked_count, 0) + ?)', [$plan_qudao_tempCount[$key]]);
}
$u = $query->increment('used_count', $plan_qudao_tempCount[$key]);
if(!$u){
$up_plan_count_all_success=false;
break;
}
$appointment_use_plan_detail_arr[]=[
"roster_detail_count_id"=>$planCount->id,
"count"=>$plan_qudao_tempCount[$key],
];
}
if ($up_plan_count_all_success) {
foreach ($roster_detail_counts as $key => $planCount) {
$cha = DB::table('s_source_roster_detail_count')->where(['id' => $planCount->id])->first();
if ($cha->count < $cha->used_count + ($cha->locked_count ?? 0)) {
DB::rollBack();
return \Yz::echoError1('操作失败1');
if ($is_emergency!==1) {
if ($cha->count < $cha->used_count + ($cha->locked_count ?? 0)) {
DB::rollBack();
return \Yz::echoError1('操作失败1');
}
}
}
}else{
DB::rollBack();
@ -405,10 +444,11 @@ WHERE
'xuhao' => $xvhao,
'appointment_type_id' => $appointment_type,
'qudao_appointment_type_id' => $appointment_type,
'appointment_use_plan_detail'=>$appointment_use_plan_detail_arr
];
$list_all_success =true;
$offset = 0;
$u_mainList=false;
foreach($plan_qudao_tempCount as $key=>$length){
if($length==0) continue;
$u_data['appointment_type_id']= $appointment_types[$key];
@ -445,7 +485,52 @@ WHERE
// if(count($mainlistids)>1) return \Yz::echoError1('请选择1条医嘱改约暂不支持批量');
$note = "改约";
//如果是改约,则恢复原来的数量
$u_old = DB::table('s_source_roster_detail_count')->where(['roster_detail_id' => $oldMainInfo->roster_id, 'appointment_type_id' => $oldMainInfo->appointment_type_id, ['used_count', '>', 0]])->decrement('used_count');
$useDetailJson = $oldMainInfo->appointment_use_plan_detail;
if (empty($useDetailJson)) {
return \Yz::echoError1('取消失败:缺少占位明细数据,无法精确回退,请联系管理员');
}
$useDetails = json_decode($useDetailJson, true);
if (!is_array($useDetails)) {
return \Yz::echoError1('取消失败:占位明细数据格式错误');
}
foreach ($useDetails as $detail) {
$rosterDetailCountId = $detail['roster_detail_count_id'] ?? null;
$countToRelease = (int)($detail['count'] ?? 0);
if (!$rosterDetailCountId || $countToRelease <= 0) {
continue;
}
$affected = DB::table('s_source_roster_detail_count')
->where(['id' => $rosterDetailCountId])
->lockForUpdate()
->where('used_count', '>=', $countToRelease) // 防御性编程:确保不会减成负数
->decrement('used_count', $countToRelease);
if ($affected == 0) {
// 如果 affected 为 0可能是记录不存在或者 used_count 不足
$checkRecord = DB::table('s_source_roster_detail_count')->where(['id' => $rosterDetailCountId])->first();
if (!$checkRecord) {
Log::error("改约-号源记录缺失", [
'MainListId' => $oldMainInfo->id,
'count_id' => $rosterDetailCountId
]);
}
if ($checkRecord->used_count < $countToRelease) {
// 【关键修改】:余额不足,但不阻断取消
$actualRelease = $checkRecord->used_count; // 最多只能退这么多
if ($actualRelease > 0) {
// 把剩余的余额全部退完,强制设为 0 (或者 decrement actualRelease)
DB::table('s_source_roster_detail_count')
->where(['id' => $rosterDetailCountId])
->update(['used_count' => 0]); // 直接置零,防止浮点数或并发问题
}
// 记录严重报警日志
$msg = "数据不一致警告:订单 {$oldMainInfo->id} 试图退还 {$countToRelease} 个名额,但渠道 {$rosterDetailCountId} 仅剩 {$checkRecord->used_count} 个。";
Log::error($msg);
}
}
}
}
@ -557,117 +642,51 @@ WHERE
if ($mainInfo->list_status != 1) {
return \Yz::echoError1('该记录无法取消,当前状态:' . $mainInfo->list_status);
}
// 3. 获取检查项目信息(判断是否可用)
$item = DB::table('s_check_item')
->where(['item_code' => $mainInfo->entrust_code, 'status' => 1, 'is_del' => 0])
->first();
if (!$item) {
return \Yz::echoError1('此检查项目不可用');
}
$zhanweiCount = (int)$item->use_seats; // 该预约占用的号源数量
$rosterId = $mainInfo->roster_id; // 排班详情ID
if ($zhanweiCount <= 0) {
$zhanweiCount = 1; // 防止配置错误默认至少占1个号源
$useDetailJson = $mainInfo->appointment_use_plan_detail;
if (empty($useDetailJson)) {
return \Yz::echoError1('取消失败:缺少占位明细数据,无法精确回退,请联系管理员');
}
// 4. 查询排班详情并加锁(确保号源操作安全)
$planInfo = DB::table('s_source_roster_detail')
->where(['id' => $rosterId])
->lockForUpdate()
->first();
if (!$planInfo) {
return \Yz::echoError1('号源排班信息不存在');
$useDetails = json_decode($useDetailJson, true);
if (!is_array($useDetails)) {
return \Yz::echoError1('取消失败:占位明细数据格式错误');
}
foreach ($useDetails as $detail) {
$rosterDetailCountId = $detail['roster_detail_count_id'] ?? null;
$countToRelease = (int)($detail['count'] ?? 0);
// 5. 获取当前预约类型的合并渠道配置
$appointment_type_link = DB::table('s_appointment_type_ratio')
->where([
'department_id' => $planInfo->department_id,
'appointment_type_id' => $mainInfo->appointment_type_id
])
->first();
// 构建可用的预约类型列表(包含自身 + 合并渠道)
$appointment_types = [$mainInfo->appointment_type_id];
if (!empty($appointment_type_link->link)) {
$linkTypes = json_decode($appointment_type_link->link, true);
if (is_array($linkTypes)) {
$appointment_types = array_merge($appointment_types, $linkTypes);
if (!$rosterDetailCountId || $countToRelease <= 0) {
continue;
}
$appointment_types = array_unique($appointment_types); // 去重
sort($appointment_types);
}
// 6. 获取当前渠道已使用的号源数量
$currentCountRecord = DB::table('s_source_roster_detail_count')
->where(['roster_detail_id' => $rosterId, 'appointment_type_id' => $mainInfo->appointment_type_id])
->lockForUpdate() // 加锁
->first();
if (!$currentCountRecord) {
return \Yz::echoError1('当前渠道号源计数记录不存在');
}
$usedCurrent = (int)$currentCountRecord->used_count;
// 7. 开始释放号源:优先从当前渠道释放,不足则从合并渠道扣除
$remainingRelease = $zhanweiCount; // 还需释放的数量
// 先从当前预约类型渠道释放
if ($usedCurrent >= $remainingRelease) {
// 当前渠道足够释放
DB::table('s_source_roster_detail_count')
->where(['roster_detail_id' => $rosterId, 'appointment_type_id' => $mainInfo->appointment_type_id])
->decrement('used_count', $remainingRelease);
$remainingRelease = 0;
} else {
// 当前渠道不够,全部释放当前渠道
$releasedFromCurrent = $usedCurrent;
DB::table('s_source_roster_detail_count')
->where(['roster_detail_id' => $rosterId, 'appointment_type_id' => $mainInfo->appointment_type_id])
->update(['used_count' => 0]);
$remainingRelease -= $releasedFromCurrent;
}
// 如果还有剩余要释放的号源,从合并渠道中扣除
if ($remainingRelease > 0) {
foreach ($appointment_types as $typeId) {
// 跳过当前已处理的渠道
if ($typeId == $mainInfo->appointment_type_id) {
continue;
}
$mergeCountRecord = DB::table('s_source_roster_detail_count')
->where(['roster_detail_id' => $rosterId, 'appointment_type_id' => $typeId])
->lockForUpdate()
->first();
if (!$mergeCountRecord || $mergeCountRecord->used_count <= 0) {
continue;
$affected = DB::table('s_source_roster_detail_count')
->where(['id' => $rosterDetailCountId])
->lockForUpdate()
->where('used_count', '>=', $countToRelease) // 防御性编程:确保不会减成负数
->decrement('used_count', $countToRelease);
if ($affected == 0) {
// 如果 affected 为 0可能是记录不存在或者 used_count 不足
$checkRecord = DB::table('s_source_roster_detail_count')->where(['id' => $rosterDetailCountId])->first();
if (!$checkRecord) {
Log::error("取消预约-记录缺失", [
'MainListId' => $MainListId,
'count_id' => $rosterDetailCountId
]);
}
if ($checkRecord->used_count < $countToRelease) {
// 【关键修改】:余额不足,但不阻断取消
$actualRelease = $checkRecord->used_count; // 最多只能退这么多
if ($actualRelease > 0) {
// 把剩余的余额全部退完,强制设为 0 (或者 decrement actualRelease)
DB::table('s_source_roster_detail_count')
->where(['id' => $rosterDetailCountId])
->update(['used_count' => 0]); // 直接置零,防止浮点数或并发问题
}
$deduct = min($mergeCountRecord->used_count, $remainingRelease);
DB::table('s_source_roster_detail_count')
->where(['roster_detail_id' => $rosterId, 'appointment_type_id' => $typeId])
->decrement('used_count', $deduct);
$remainingRelease -= $deduct;
if ($remainingRelease <= 0) {
break;
// 记录严重报警日志
$msg = "数据不一致警告:订单 {$MainListId} 试图退还 {$countToRelease} 个名额,但渠道 {$rosterDetailCountId} 仅剩 {$checkRecord->used_count} 个。";
Log::error($msg);
}
}
// 如果最终仍无法完全释放所需号源数量,说明数据异常(理论上不应发生)
if ($remainingRelease > 0) {
DB::rollBack();
return \Yz::echoError1('号源释放失败:合并渠道号源不足,无法完成释放');
}
}
// 8. 更新主表状态为“已取消”0

@ -16,6 +16,12 @@ return [
'2'=>'急诊',
'3'=>'体检',
],
'特殊权限'=>[
[
'name'=>'加急预约',
'key'=>'emergency',
]
]
],
/*

@ -67,6 +67,7 @@
:class="{'ZhenShiButton zhenshiButton_active': activeZhenShi === item,'ZhenShiButton': activeZhenShi !== item}"
@click="zhenshiClick(item)">{{item}}</div>
<div class="shuxian"></div>
<el-button v-if="canEmergency" class="do_button" type="danger" @click="YuYueButtonClick(111)"></el-button>
<el-button class="do_button" type="success" @click="YuYueButtonClick(1)"></el-button>
<el-button class="do_button" type="warning" @click="YuYueButtonClick(2)"></el-button>
<el-button class="do_button" type="danger" @click="CancelYuYueFunc()"></el-button>
@ -122,7 +123,7 @@
import {
ref,
onMounted,
watch
watch,computed
} from 'vue'
import {
getMainDetail,
@ -156,6 +157,10 @@
do_user: {
default:'',
type: [String, Number]
},
special_privileges:{
type: Array,
default: () => []
}
})
let entrustTableRef = ref(null)
@ -184,6 +189,9 @@
const dayOfWeek = date.getDay();
return days[dayOfWeek]
}
const canEmergency = computed(() => { //
return props.special_privileges.includes('emergency');
});
const handleSelectionChange = (selection) => {
selectedMianListId.value = []
selectedEntrustId.value = []
@ -400,6 +408,7 @@
if (res.status) {
let plans = res.data.plan_list
let zhanWeiCount=res.data.zhanWeiCount
let is_emergency=res.data.is_emergency
if (plans.length > 0) {
if (activeZhenShi.value == '') activeZhenShi.value = plans[0].department_resources_name
@ -439,7 +448,9 @@
if(selectedPlanId.value == 0 || selectedPlanId.value == '' || selectedPlanId.value==null && matchingPlan.id==res.data.earliestPlan){
selectedPlanId.value=matchingPlan.id
}
}
if(is_emergency===true && matchingPlan.plan_enable===true && canEmergency.value==true){
plan_enable = true
}
row[date] = {
use_count: `${matchingPlan.used_count}/${matchingPlan.count}`,
@ -575,6 +586,7 @@
appointment_type: props.appointment_type,
dotype: type, //12,
do_user:props.do_user,
is_emergency:type==111?1:null
}).then(res => {
planLoading.value = false

@ -45,15 +45,27 @@
@size-change="PageSizeChange" @current-change="PageCurrentChange" />
</div>
<el-dialog v-model="dialogVisible" title="用户" width="30%">
<el-form :model="UserInfo" label-width="50px" v-loading="loading">
<el-dialog v-model="dialogVisible" title="用户" width="35%">
<el-form :model="UserInfo" label-width="100px" v-loading="loading">
<el-form-item label="分组">
<el-select v-model="UserInfo.groupId" placeholder="请选择分组">
<el-form-item label="角色">
<el-select v-model="UserInfo.groupId" placeholder="请选择角色">
<el-option v-for="(item,index) in groupList" :key="index" :label="item.group_name"
:value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="特殊权限">
<el-checkbox-group v-model="UserInfo.special_privileges">
<el-checkbox
v-for="(item, index) in special_privileges_list"
:key="index"
:label="item.key"
>
{{ item.name }}
</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="名称">
<el-input v-model="UserInfo.cname" />
</el-form-item>
@ -141,7 +153,9 @@
uname: '',
id: 0,
status: 1,
special_privileges:[]
})
let special_privileges_list=ref([]);
let searchInfo = ref({
status: null,
@ -192,6 +206,7 @@
type: 'enable'
}).then(res => {
groupList.value = res.list
special_privileges_list.value=res.special_privileges_list
})
}
const add = (id1 = '') => {
@ -203,6 +218,7 @@
uname: '',
id: id1,
status: 1,
special_privileges: []
}
GetGroupList()
if (id1 > 0) {
@ -215,6 +231,8 @@
UserInfo.value.uname = res.info[0].username
UserInfo.value.id = res.info[0].id
UserInfo.value.status = res.info[0].status
UserInfo.value.special_privileges=res.info[0].special_privileges
})
}

@ -4,7 +4,7 @@
<iframe id="bdIframe" :src="selectedUrl" class="iframe" :key="YuYueKey" scrolling="auto"></iframe>
</el-dialog>
<el-dialog v-model="YuYueVueDialogVisible" width="100%" @close="closeYuYueDialog()">
<YuYue202506 :reg_num="tableSelected[0].reg_num" :entrust_ids="entrust_ids" :key="YuYueKey" :episode_id="tableSelected[0].episodeid" appointment_type="4" :dotype="do_type" :do_user="loginUserinfo.id"></YuYue202506>
<YuYue202506 :reg_num="tableSelected[0].reg_num" :entrust_ids="entrust_ids" :key="YuYueKey" :episode_id="tableSelected[0].episodeid" appointment_type="4" :dotype="do_type" :do_user="loginUserinfo.id" :special_privileges="loginUserinfo.special_privileges"></YuYue202506>
</el-dialog>
<div class="head">
<el-row>

Loading…
Cancel
Save