This commit is contained in:
gr
2026-10-15 11:03:54 +08:00
parent 70ddc1a70a
commit bb28d93cec
64 changed files with 12747 additions and 420 deletions
@@ -0,0 +1,143 @@
<?php
declare(strict_types=1);
namespace app\common\service\tcm;
use think\facade\Db;
use think\facade\Log;
/**
* 复诊到期快照业务钩子(订单 / 挂号 / 指派 / 诊单变更 / 过号 cron)。
*
* 所有方法整体 try/catch 并 Log::warning,绝不向调用方抛异常、不改变主流程返回值;
* 任一钩子漏接由 30 分钟增量 cron(tcm:revisit-due-scan --mode=incremental)与列表自愈兜底。
*
* 接入点(由其它代理在业务 Logic 的事务提交之后调用):
* - PrescriptionOrderLogic create/editTime/withdraw/complete(9,10)/refund、GancaoCallbackController case 91/92 → onOrderChanged
* - AppointmentLogic create/cancel/complete/adminEdit → onAppointmentChanged
* - DiagnosisLogic::assign、AutoAssignPendingDiagnosis、AutoAssignLogLogic 回退 → onAssistantChanged
* - DiagnosisLogic::setRevisitSlotStartOffset 等诊单字段变更 → onDiagnosisChanged
* - UpdateAppointmentStatus 两条 Db::execute 之后 → onAppointmentStatusCronDone
*/
final class RevisitDueHook
{
/**
* 业务订单变化(新建 / 改下单时间 / 撤回 / 完成为 9,10 / 退款 / 甘草回调置 4)。
* diagnosisId 为 0 时按 orderId 反查诊单。
*/
public static function onOrderChanged(int $diagnosisId, int $orderId = 0, string $reason = ''): void
{
try {
if ($diagnosisId <= 0 && $orderId > 0) {
$diagnosisId = (int) Db::name('tcm_prescription_order')->where('id', $orderId)->value('diagnosis_id');
}
if ($diagnosisId <= 0) {
return;
}
RevisitDueService::refreshDiagnoses([$diagnosisId], [
'reason' => self::reasonTag('order', $reason),
'operator_id' => self::currentAdminId(),
]);
} catch (\Throwable $e) {
Log::warning(sprintf('复诊到期钩子 onOrderChanged 失败 diagnosis_id=%d order_id=%d: %s', $diagnosisId, $orderId, $e->getMessage()));
}
}
/**
* 挂号变化(创建 / 取消 / 完成 / 后台编辑)。diagnosisId 为 0 时按 appointmentId 反查(patient_id 存诊单 id)。
*/
public static function onAppointmentChanged(int $diagnosisId, int $appointmentId = 0, string $reason = ''): void
{
try {
if ($diagnosisId <= 0 && $appointmentId > 0) {
$diagnosisId = (int) Db::name('doctor_appointment')->where('id', $appointmentId)->value('patient_id');
}
if ($diagnosisId <= 0) {
return;
}
RevisitDueService::refreshDiagnoses([$diagnosisId], [
'reason' => self::reasonTag('appointment', $reason),
'operator_id' => self::currentAdminId(),
]);
} catch (\Throwable $e) {
Log::warning(sprintf('复诊到期钩子 onAppointmentChanged 失败 diagnosis_id=%d appointment_id=%d: %s', $diagnosisId, $appointmentId, $e->getMessage()));
}
}
/**
* 医助变更(指派 / 继承 / 释放 to=0 / 自动分配 / 回退)。toAssistantId 仅用于流水 payload,实际以库内 tcm_diagnosis.assistant_id 为准。
*/
public static function onAssistantChanged(int $diagnosisId, int $toAssistantId, string $reason = ''): void
{
try {
if ($diagnosisId <= 0) {
return;
}
$tag = self::reasonTag('assign', $reason);
if ($toAssistantId >= 0) {
$tag .= ':to=' . $toAssistantId;
}
RevisitDueService::refreshDiagnoses([$diagnosisId], [
'reason' => $tag,
'operator_id' => self::currentAdminId(),
]);
} catch (\Throwable $e) {
Log::warning(sprintf('复诊到期钩子 onAssistantChanged 失败 diagnosis_id=%d to=%d: %s', $diagnosisId, $toAssistantId, $e->getMessage()));
}
}
/**
* 诊单其它字段变更(如 revisit_slot_start_offset、status 禁用/启用、软删)。
*/
public static function onDiagnosisChanged(int $diagnosisId, string $reason = ''): void
{
try {
if ($diagnosisId <= 0) {
return;
}
RevisitDueService::refreshDiagnoses([$diagnosisId], [
'reason' => self::reasonTag('diagnosis', $reason),
'operator_id' => self::currentAdminId(),
]);
} catch (\Throwable $e) {
Log::warning(sprintf('复诊到期钩子 onDiagnosisChanged 失败 diagnosis_id=%d: %s', $diagnosisId, $e->getMessage()));
}
}
/**
* 过号 cron(update_appointment_status)执行之后:重评 closed_state=2 / appointment_state=1 的行,使当天过号即进标红/弹窗。
*/
public static function onAppointmentStatusCronDone(): void
{
try {
RevisitDueService::reevaluateBookedRows();
} catch (\Throwable $e) {
Log::warning('复诊到期钩子 onAppointmentStatusCronDone 失败: ' . $e->getMessage());
}
}
/** 流水 payload 的 reason 标签:<来源>[:<说明>],限 64 字符 */
private static function reasonTag(string $source, string $reason): string
{
$reason = trim($reason);
return mb_substr($reason !== '' ? $source . ':' . $reason : $source, 0, 64);
}
/** 当前登录管理员 id(CLI / 回调无登录态时为 0),任何异常均视为 0 */
private static function currentAdminId(): int
{
try {
$req = request();
$admin = $req->adminInfo ?? null;
if (is_array($admin)) {
return (int) ($admin['admin_id'] ?? 0);
}
} catch (\Throwable $e) {
// CLI 场景无请求上下文,忽略
}
return 0;
}
}