HIS回写逻辑调整

dev
鹿和sa0ChunLuyu 17 hours ago
parent a3cdd01594
commit 2894b6e3c6

@ -1304,6 +1304,63 @@ public function YuYue($planid, $appointment_type, $mainlistids, $do_type,$is_eme
$additionalNeeded = 0;
$zhanweiCount = 0;
// === HIS 回写执行科室(事务前,失败阻断预约)===
$writebackResults = $this->preYuYueWriteback($oldMainInfos, $planInfo);
// 全部回写失败 → 直接返回,不进事务
if (empty($writebackResults['success'])) {
return \Yz::Return(false, 'HIS回写执行科室失败无法完成预约', [
'fail_items' => $writebackResults['fail_items'],
]);
}
// 部分回写失败 → 过滤 mainlistids 及关联数据结构
if (!empty($writebackResults['fail'])) {
$successIds = array_keys($writebackResults['success']);
$failIds = array_keys($writebackResults['fail']);
// 过滤 mainlistids
$mainlistids = array_values(array_intersect($mainlistids, $successIds));
// 过滤 oldMainInfos
$oldMainInfos = array_filter($oldMainInfos, function($info) use ($successIds) {
return in_array($info->id, $successIds);
});
// 过滤 itemSeatsData
$itemSeatsData = array_intersect_key($itemSeatsData, array_flip($successIds));
// 重建 huchiList剔除失败项的互斥关系
$huchiList = [];
foreach ($oldMainInfos as $key1 => $value1) {
$cha_hc = DB::table('s_huchi')->where('is_del', 0)
->where(function ($q) use ($value1) {
$q->where(['code1' => $value1->entrust_code])->orWhere('code2', $value1->entrust_code);
})->get();
foreach ($cha_hc as $hc) {
$huchiList[] = [$hc->code1, $hc->code2];
}
}
// 重建共享号源预计算
$shareSlotIds = [];
$shareSlotMax = 0;
foreach ($itemSeatsData as $id => $data) {
if (!empty($data['is_share_slot'])) {
$shareSlotIds[] = $id;
$shareSlotMax = max($shareSlotMax, $data['use_seats']);
}
}
$maxShareSlotId = null;
$maxShareSeats = 0;
foreach ($itemSeatsData as $id => $data) {
if (!empty($data['is_share_slot']) && $data['use_seats'] > $maxShareSeats) {
$maxShareSeats = $data['use_seats'];
$maxShareSlotId = $id;
}
}
}
DB::beginTransaction();
try {
// 改约时提前记录旧排班 ID在 s_list 更新后校正
@ -1715,6 +1772,12 @@ public function YuYue($planid, $appointment_type, $mainlistids, $do_type,$is_eme
'is_emergency' => $is_emergency
];
// 回写成功项追加执行科室字段
if (isset($writebackResults['success'][$id]) && !empty($writebackResults['success'][$id]['newDeptCode'])) {
$u_data['RISRAcceptDeptCode'] = $writebackResults['success'][$id]['newDeptCode'];
$u_data['implement_department'] = $writebackResults['success'][$id]['newDeptName'];
}
Log::info('[YuYueDebug] 保存预约 detail', [
'id' => $id,
'entrust' => $mainInfo->entrust ?? 'unknown',
@ -1752,7 +1815,15 @@ public function YuYue($planid, $appointment_type, $mainlistids, $do_type,$is_eme
'new_status' => 1,
'create_user' => $do_user,
'note' => $note,
'data' => json_encode($savedDataMap[$oldMainInfo->id] ?? [])
'data' => json_encode(array_merge(
$savedDataMap[$oldMainInfo->id] ?? [],
isset($writebackResults['success'][$oldMainInfo->id]) ? [
'old_RISRAcceptDeptCode' => $writebackResults['success'][$oldMainInfo->id]['oldDeptCode'],
'new_RISRAcceptDeptCode' => $writebackResults['success'][$oldMainInfo->id]['newDeptCode'],
'old_implement_department' => $writebackResults['success'][$oldMainInfo->id]['oldDeptName'],
'new_implement_department' => $writebackResults['success'][$oldMainInfo->id]['newDeptName'],
] : []
), JSON_UNESCAPED_UNICODE)
]);
}
@ -1765,21 +1836,14 @@ public function YuYue($planid, $appointment_type, $mainlistids, $do_type,$is_eme
DB::commit();
// HIS 回写通知
try {
$this->notifyHisAfterYuYue($oldMainInfos, $planInfo);
} catch (\Exception $e) {
Log::error('HIS 回写通知异常', [
'planid' => $planid,
'mainlistids' => $mainlistids,
'error' => $e->getMessage()
]);
}
if(config('app.globals.预约完成短信通知')==1){
$this->SendMsg($oldMainInfos,$do_type);
}
return \Yz::Return(true, '预约成功', ['planid' => $planid, 'mainlistids' => $mainlistids]);
return \Yz::Return(true, '预约成功', [
'planid' => $planid,
'mainlistids' => $mainlistids,
'fail_items' => $writebackResults['fail_items'] ?? [],
]);
@ -1787,6 +1851,16 @@ public function YuYue($planid, $appointment_type, $mainlistids, $do_type,$is_eme
} catch (\Exception $e) {
DB::rollBack();
// 补偿回写:恢复 HIS 侧执行科室为旧值
try {
$this->rollbackHisWriteback($writebackResults);
} catch (\Exception $e2) {
Log::error('补偿回写整体异常', [
'error' => $e2->getMessage(),
]);
}
return \Yz::echoError1('预约异常' . $e->getMessage());
}
@ -2326,20 +2400,81 @@ public function calculateUsedCount($rosterId, $correct = 0)
}
/**
* 预约完成后通知 HIS 回写执行科室
* 在事务提交后调用,异常不影响主流程
* 预约事务前 HIS 回写执行科室
* 逐条调用回写接口,收集成功/失败结果,不更新 s_list事务内统一写入
* 非正式环境默认全部成功
*
* @param array $oldMainInfos 事务前构建的 s_list 行数组
* @param object $planInfo 排班信息
* @return array {success: [id => info], fail: [id => info], fail_items: [[name, reason], ...]}
*/
private function notifyHisAfterYuYue($oldMainInfos, $planInfo)
private function preYuYueWriteback($oldMainInfos, $planInfo)
{
// 仅内网正式环境192.168.80.76)执行 HIS 回写
$results = [
'success' => [],
'fail' => [],
'fail_items' => [],
];
// 非正式环境逐条随机模拟回写结果80% 概率失败),用于测试失败链路
// 非正式环境跳过回写,全部视为成功
if (request()->getHost() !== '192.168.80.76') {
// // [测试逻辑] 开发环境随机模拟 HIS 回写结果80% 失败),已注释
// Log::info('HIS 回写:当前环境非正式环境,启动随机模拟', [
// 'host' => request()->getHost()
// ]);
// foreach ($oldMainInfos as $mainInfo) {
// $rand = mt_rand(1, 100);
// if ($rand <= 80) {
// // 模拟回写失败
// Log::info('HIS 回写模拟失败', [
// 'id' => $mainInfo->id,
// 'rand' => $rand,
// ]);
// $results['fail'][$mainInfo->id] = [
// 'name' => $mainInfo->entrust,
// 'reason' => 'HIS回写执行科室失败无法完成预约',
// ];
// $results['fail_items'][] = [
// 'name' => $mainInfo->entrust,
// 'reason' => 'HIS回写执行科室失败无法完成预约',
// ];
// } else {
// // 模拟回写成功
// Log::info('HIS 回写模拟成功', [
// 'id' => $mainInfo->id,
// 'rand' => $rand,
// ]);
// $results['success'][$mainInfo->id] = [
// 'mainInfo' => $mainInfo,
// 'oldDeptCode' => '',
// 'oldDeptName' => '',
// 'newDeptCode' => '',
// 'newDeptName' => '',
// 'body' => null,
// ];
// }
// }
// return $results;
// [正式逻辑] 非正式环境全部视为成功
Log::info('HIS 回写跳过:当前环境非内网正式环境', [
'host' => request()->getHost()
]);
return;
foreach ($oldMainInfos as $mainInfo) {
$results['success'][$mainInfo->id] = [
'mainInfo' => $mainInfo,
'oldDeptCode' => '',
'oldDeptName' => '',
'newDeptCode' => '',
'newDeptName' => '',
'body' => null,
];
}
return $results;
}
// 1. 获取 execDeptCode / execDeptName本地数据库
// 1. 获取新执行科室
$execDeptCode = '';
$execDeptName = '';
$resource = DB::table('s_department_resources')
@ -2359,30 +2494,51 @@ private function notifyHisAfterYuYue($oldMainInfos, $planInfo)
}
if (empty($execDeptCode)) {
Log::warning('HIS 回写通知跳过:未找到执行科室信息', [
Log::warning('HIS 回写跳过:未找到执行科室信息', [
'resources_id' => $planInfo->resources_id
]);
return;
// 没有执行科室信息,全部视为成功(不回写)
foreach ($oldMainInfos as $mainInfo) {
$results['success'][$mainInfo->id] = [
'mainInfo' => $mainInfo,
'oldDeptCode' => $mainInfo->RISRAcceptDeptCode ?? '',
'oldDeptName' => $mainInfo->implement_department ?? '',
'newDeptCode' => $execDeptCode,
'newDeptName' => $execDeptName,
'body' => null,
];
}
return $results;
}
// 2. 按 app_num 分组去重
// 2. 按 app_num 分组
$grouped = [];
foreach ($oldMainInfos as $mainInfo) {
$appNum = $mainInfo->app_num;
if (empty($appNum)) continue;
if (!isset($grouped[$appNum])) {
$grouped[$appNum] = [
'episodeid' => $mainInfo->episodeid,
'episodeid' => $mainInfo->episodeid,
'patient_type' => $mainInfo->patient_type,
'items' => [],
'items' => [],
];
}
$grouped[$appNum]['items'][] = $mainInfo;
}
if (empty($grouped)) {
Log::warning('HIS 回写通知跳过:没有有效的申请单号');
return;
Log::warning('HIS 回写跳过:没有有效的申请单号');
foreach ($oldMainInfos as $mainInfo) {
$results['fail'][$mainInfo->id] = [
'name' => $mainInfo->entrust,
'reason' => 'HIS回写执行科室失败无法完成预约',
];
$results['fail_items'][] = [
'name' => $mainInfo->entrust,
'reason' => 'HIS回写执行科室失败无法完成预约',
];
}
return $results;
}
// 3. 遍历每组,调 GetApplyDetail 获取申请单数据(带缓存)
@ -2394,42 +2550,69 @@ private function notifyHisAfterYuYue($oldMainInfos, $planInfo)
} catch (\Exception $e) {
Log::warning('HIS 查询申请单异常', [
'app_num' => $appNum,
'error' => $e->getMessage()
'error' => $e->getMessage(),
]);
foreach ($group['items'] as $mainInfo) {
$results['fail'][$mainInfo->id] = [
'name' => $mainInfo->entrust,
'reason' => 'HIS回写执行科室失败无法完成预约',
];
$results['fail_items'][] = [
'name' => $mainInfo->entrust,
'reason' => 'HIS回写执行科室失败无法完成预约',
];
}
continue;
}
if ($hisRes['code'] != 200 || empty($hisRes['data'])) {
Log::warning('HIS 查询申请单失败', [
'app_num' => $appNum,
'response' => $hisRes
'app_num' => $appNum,
'response' => $hisRes,
]);
foreach ($group['items'] as $mainInfo) {
$results['fail'][$mainInfo->id] = [
'name' => $mainInfo->entrust,
'reason' => 'HIS回写执行科室失败无法完成预约',
];
$results['fail_items'][] = [
'name' => $mainInfo->entrust,
'reason' => 'HIS回写执行科室失败无法完成预约',
];
}
continue;
}
$hisData = $hisRes['data'][0] ?? null;
if (!$hisData) {
Log::warning('HIS 查询返回数据为空', ['app_num' => $appNum]);
foreach ($group['items'] as $mainInfo) {
$results['fail'][$mainInfo->id] = [
'name' => $mainInfo->entrust,
'reason' => 'HIS回写执行科室失败无法完成预约',
];
$results['fail_items'][] = [
'name' => $mainInfo->entrust,
'reason' => 'HIS回写执行科室失败无法完成预约',
];
}
continue;
}
// 4. 提取申请单级数据comboNo 从 HIS 返回数据获取最新)
// 4. 提取申请单级数据
$comboNo = $hisData['requestNo'] ?? '';
// 5. 遍历每条 s_list匹配 entrust_code 获取项目级数据
// 5. 遍历每条 s_list匹配 entrust_code
foreach ($group['items'] as $mainInfo) {
// 住院取 episodeid门诊取 reg_num
$inpatientNo = $mainInfo->patient_type == 0 ? $mainInfo->episodeid : $mainInfo->episodeid;
$entrustCode = $mainInfo->entrust_code;
$moOrder = '';
$hisOrderNo = '';
$inpatientNo = $mainInfo->episodeid;
$entrustCode = $mainInfo->entrust_code;
$moOrder = '';
$hisOrderNo = '';
// 在 itemList 中匹配 termCode == entrust_code
if (!empty($hisData['itemList']) && is_array($hisData['itemList'])) {
foreach ($hisData['itemList'] as $item) {
if (($item['termCode'] ?? '') == $entrustCode) {
$moOrder = $item['orderNo'] ?? '';
$moOrder = $item['orderNo'] ?? '';
$hisOrderNo = $item['chargeList'][0]['hisOrderNo'] ?? '';
break;
}
@ -2439,65 +2622,119 @@ private function notifyHisAfterYuYue($oldMainInfos, $planInfo)
if (empty($moOrder)) {
Log::warning('HIS 回写:未匹配到 itemList 中的项目', [
'entrust_code' => $entrustCode,
'app_num' => $appNum
'app_num' => $appNum,
]);
$results['fail'][$mainInfo->id] = [
'name' => $mainInfo->entrust,
'reason' => 'HIS回写执行科室失败无法完成预约',
];
$results['fail_items'][] = [
'name' => $mainInfo->entrust,
'reason' => 'HIS回写执行科室失败无法完成预约',
];
continue;
}
// 6. 组装并调用 HIS 回写接口
$body = [
'moOrder' => $moOrder,
'moOrder' => $moOrder,
'execDeptCode' => $execDeptCode,
'execDeptName' => $execDeptName,
'inpatientNo' => $inpatientNo,
'hisOrderNo' => $hisOrderNo,
'comboNo' => $comboNo,
'itemCode' => $entrustCode,
'inpatientNo' => $inpatientNo,
'hisOrderNo' => $hisOrderNo,
'comboNo' => $comboNo,
'itemCode' => $entrustCode,
];
// 记录旧科室
$oldDeptCode = $mainInfo->RISRAcceptDeptCode ?? '';
$oldDeptName = $mainInfo->implement_department ?? '';
try {
$hisRes = HisController::Post('/roc/api/v1/qhd/common/yjyy/updateExecDept', $body);
if ($hisRes['code'] == 200) {
// 记录变更前的旧值
$oldDeptCode = $mainInfo->RISRAcceptDeptCode ?? '';
$oldDeptName = $mainInfo->implement_department ?? '';
DB::table('s_list')
->where('id', $mainInfo->id)
->update([
'RISRAcceptDeptCode' => $execDeptCode,
'implement_department' => $execDeptName,
]);
DB::table('s_list_log')->insert([
'list_id' => $mainInfo->id,
'reg_num' => $mainInfo->reg_num,
'old_status' => $mainInfo->list_status,
'new_status' => $mainInfo->list_status,
'create_user' => 'sys_auto',
'note' => 'HIS回写更新执行科室',
'data' => json_encode([
'old_RISRAcceptDeptCode' => $oldDeptCode,
'new_RISRAcceptDeptCode' => $execDeptCode,
'old_implement_department' => $oldDeptName,
'new_implement_department' => $execDeptName,
], JSON_UNESCAPED_UNICODE),
]);
HisController::Post('/roc/api/v1/qhd/common/yjyy/updateExecDept', $body);
// 回写成功,记录完整信息供事务内写入和补偿使用
$results['success'][$mainInfo->id] = [
'mainInfo' => $mainInfo,
'oldDeptCode' => $oldDeptCode,
'oldDeptName' => $oldDeptName,
'newDeptCode' => $execDeptCode,
'newDeptName' => $execDeptName,
'body' => $body,
'visitSqNo' => $visitSqNo,
'appNum' => $appNum,
'patientType' => $group['patient_type'],
];
// 同步更新 ROC 缓存中对应检查项目的执行科室
HisController::UpdateApplyDetailCache(
$visitSqNo, $appNum, $group['patient_type'], '本地',
$entrustCode, $execDeptCode, $execDeptName
);
}
// 同步更新 ROC 缓存为新科室
HisController::UpdateApplyDetailCache(
$visitSqNo, $appNum, $group['patient_type'], '本地',
$entrustCode, $execDeptCode, $execDeptName
);
} catch (\Exception $e) {
Log::error('HIS 回写接口请求异常', [
'body' => $body,
'error' => $e->getMessage()
'body' => $body,
'error' => $e->getMessage(),
]);
$results['fail'][$mainInfo->id] = [
'name' => $mainInfo->entrust,
'reason' => 'HIS回写执行科室失败无法完成预约',
];
$results['fail_items'][] = [
'name' => $mainInfo->entrust,
'reason' => 'HIS回写执行科室失败无法完成预约',
];
}
}
}
return $results;
}
/**
* 补偿回写:事务回滚后恢复 HIS 侧执行科室为旧值
* 仅处理回写成功且新旧科室不一致的项目
*/
private function rollbackHisWriteback($writebackResults)
{
// 非正式环境跳过补偿
if (request()->getHost() !== '192.168.80.76') {
return;
}
foreach ($writebackResults['success'] as $id => $info) {
if (empty($info['body'])) continue;
// 新旧科室一致,无需补偿
if ($info['newDeptCode'] === $info['oldDeptCode']) {
continue;
}
// 复用 body替换为旧科室
$rollbackBody = $info['body'];
$rollbackBody['execDeptCode'] = $info['oldDeptCode'];
$rollbackBody['execDeptName'] = $info['oldDeptName'];
try {
HisController::Post('/roc/api/v1/qhd/common/yjyy/updateExecDept', $rollbackBody);
// 同步更新 ROC 缓存为旧科室
HisController::UpdateApplyDetailCache(
$info['visitSqNo'],
$info['appNum'],
$info['patientType'],
'本地',
$info['body']['itemCode'],
$info['oldDeptCode'],
$info['oldDeptName']
);
} catch (\Exception $e) {
Log::error('补偿回写HIS失败', [
'id' => $id,
'oldDeptCode' => $info['oldDeptCode'],
'error' => $e->getMessage(),
]);
}
}
}
//短信提醒

@ -1598,23 +1598,46 @@ let TanChuangMsgDialogVisible=ref(false)
}
}
}, true)
} else {
ElMessageBox.confirm(
res.msg,
'提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'error',
showCancelButton: false
}
)
.then(() => {
// HIS
if (res.data?.fail_items?.length > 0) {
let html = '<table style="width:100%;border-collapse:collapse;font-size:14px;">'
html += '<tr style="background:#f5f5f5;"><td style="padding:4px 8px;border:1px solid #ddd;font-weight:700;">序号</td><td style="padding:4px 8px;border:1px solid #ddd;font-weight:700;">项目名称</td><td style="padding:4px 8px;border:1px solid #ddd;font-weight:700;">结果</td></tr>'
res.data.fail_items.forEach((f, i) => {
html += '<tr><td style="padding:4px 8px;border:1px solid #ddd;">' + (i + 1) + '</td><td style="padding:4px 8px;border:1px solid #ddd;">' + f.name + '</td><td style="padding:4px 8px;border:1px solid #ddd;color:red;">' + f.reason + '</td></tr>'
})
html += '</table>'
resultDialogHtml.value = html
resultDialogVisible.value = true
}
} else {
//
if (res.data?.fail_items?.length > 0) {
let html = '<table style="width:100%;border-collapse:collapse;font-size:14px;">'
html += '<tr style="background:#f5f5f5;"><td style="padding:4px 8px;border:1px solid #ddd;font-weight:700;">序号</td><td style="padding:4px 8px;border:1px solid #ddd;font-weight:700;">项目名称</td><td style="padding:4px 8px;border:1px solid #ddd;font-weight:700;">结果</td></tr>'
res.data.fail_items.forEach((f, i) => {
html += '<tr><td style="padding:4px 8px;border:1px solid #ddd;">' + (i + 1) + '</td><td style="padding:4px 8px;border:1px solid #ddd;">' + f.name + '</td><td style="padding:4px 8px;border:1px solid #ddd;color:red;">' + f.reason + '</td></tr>'
})
html += '</table>'
resultDialogHtml.value = html
resultDialogVisible.value = true
} else {
ElMessageBox.confirm(
res.msg,
'提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'error',
showCancelButton: false
}
)
.then(() => {
})
.catch(() => {
})
.catch(() => {
})
}
})
}
}
})
return
}
@ -1638,7 +1661,9 @@ let TanChuangMsgDialogVisible=ref(false)
is_emergency: type == 111 ? 1 : null
})
if (res.status) {
ids.forEach(id => {
// successItems
const bookedIds = res.data?.mainlistids || ids
bookedIds.forEach(id => {
const row = entrustTableDate.value.find(v => v.id === id)
if (row) {
const timeStr = (row.period_begin_time ? row.period_begin_time.substring(0, 5) : '') + '-' + (row.period_end_time ? row.period_end_time.substring(0, 5) : '')
@ -1646,6 +1671,12 @@ let TanChuangMsgDialogVisible=ref(false)
successEntrustIds.push(row.entrust_id)
}
})
// HIS
if (res.data?.fail_items?.length > 0) {
res.data.fail_items.forEach(f => {
failItems.push({ name: f.name, msg: f.reason })
})
}
} else {
ids.forEach(id => {
const row = entrustTableDate.value.find(v => v.id === id)

Loading…
Cancel
Save