From 4f95d9182ec99c349574735089b5ad149073d725 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 17 Jul 2026 12:13:02 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/AutoAssignPendingDiagnosis.php | 88 ++++++++++++++++++- server/app/command/ExpressAutoUpdate.php | 2 +- .../app/command/GancaoSyncLogisticsRoute.php | 2 +- .../common/service/ExpressTrackingService.php | 53 ++++++++--- 4 files changed, 130 insertions(+), 15 deletions(-) diff --git a/server/app/command/AutoAssignPendingDiagnosis.php b/server/app/command/AutoAssignPendingDiagnosis.php index febfa890..6c85fcc9 100644 --- a/server/app/command/AutoAssignPendingDiagnosis.php +++ b/server/app/command/AutoAssignPendingDiagnosis.php @@ -39,6 +39,7 @@ use think\facade\Log; * * 日志:无论分配与否,每条待指派诊单都会写入 tcm_diagnosis_auto_assign_log,记录原因(为什么分配 / 为什么不分配)。 * 成功分配同时写 tcm_diagnosis_assign_log(is_inherit=0,计入次月接诊率分母),与手动指派同口径。 + * 写入时 from_assistant_id 尽量带上「一中心」原医助(指派日志中最近的一中心身份,否则回退业务单创建人若属一中心),避免原医助恒为空。 */ class AutoAssignPendingDiagnosis extends Command { @@ -499,9 +500,12 @@ class AutoAssignPendingDiagnosis extends Command $relatedPoCreatorId = 0; } + // 自动分给二中心时,原医助带上一中心身份(列表「原医助」不再恒为空) + $fromAssistantId = $this->resolveYiCenterPreviousAssistantId($diagnosisId, $relatedPoCreatorId); + Db::name('tcm_diagnosis_assign_log')->insert([ 'diagnosis_id' => $diagnosisId, - 'from_assistant_id' => 0, + 'from_assistant_id' => $fromAssistantId, 'to_assistant_id' => $toAssistantId, 'operator_admin_id' => 0, 'operator_name' => '系统自动分配', @@ -524,6 +528,88 @@ class AutoAssignPendingDiagnosis extends Command } } + /** + * 解析诊单对应的「一中心」原医助 id,供自动指派写入 from_assistant_id。 + * 优先:指派日志由新到旧,取最近出现的一中心身份(先 to 后 from); + * 回退:最新业务单创建人若属一中心则用之。跳过二中心身份,避免原医助写成上一任二中心。 + */ + private function resolveYiCenterPreviousAssistantId(int $diagnosisId, int $relatedPoCreatorId): int + { + if ($diagnosisId <= 0) { + return 0; + } + $yiDeptSet = DeptLogic::getYiCenterSubtreeDeptIdSet(); + if ($yiDeptSet === []) { + return 0; + } + + $logs = Db::name('tcm_diagnosis_assign_log') + ->where('diagnosis_id', $diagnosisId) + ->order('id', 'desc') + ->field(['from_assistant_id', 'to_assistant_id']) + ->select() + ->toArray(); + + $candidateIds = []; + foreach ($logs as $log) { + $to = (int) ($log['to_assistant_id'] ?? 0); + $from = (int) ($log['from_assistant_id'] ?? 0); + if ($to > 0) { + $candidateIds[] = $to; + } + if ($from > 0) { + $candidateIds[] = $from; + } + } + if ($relatedPoCreatorId > 0) { + $candidateIds[] = $relatedPoCreatorId; + } + $candidateIds = array_values(array_unique(array_filter($candidateIds))); + if ($candidateIds === []) { + return 0; + } + + $yiAdminSet = $this->filterAdminIdsInDeptSet($candidateIds, $yiDeptSet); + if ($yiAdminSet === []) { + return 0; + } + + foreach ($logs as $log) { + $to = (int) ($log['to_assistant_id'] ?? 0); + if ($to > 0 && isset($yiAdminSet[$to])) { + return $to; + } + $from = (int) ($log['from_assistant_id'] ?? 0); + if ($from > 0 && isset($yiAdminSet[$from])) { + return $from; + } + } + if ($relatedPoCreatorId > 0 && isset($yiAdminSet[$relatedPoCreatorId])) { + return $relatedPoCreatorId; + } + + return 0; + } + + /** + * @param list $adminIds + * @param array $deptIdSet + * + * @return array admin_id => true + */ + private function filterAdminIdsInDeptSet(array $adminIds, array $deptIdSet): array + { + if ($adminIds === [] || $deptIdSet === []) { + return []; + } + $rows = Db::name('admin_dept') + ->whereIn('admin_id', $adminIds) + ->whereIn('dept_id', array_keys($deptIdSet)) + ->column('admin_id'); + + return array_fill_keys(array_map('intval', $rows), true); + } + /** * @param array $diag 诊单行(含 id/patient_name/phone) * @param array $extra action/assistant_id/assistant_name/tier/visit2_rate/round_no/reason diff --git a/server/app/command/ExpressAutoUpdate.php b/server/app/command/ExpressAutoUpdate.php index 9ab1ba00..42a4c210 100755 --- a/server/app/command/ExpressAutoUpdate.php +++ b/server/app/command/ExpressAutoUpdate.php @@ -18,7 +18,7 @@ use think\console\Output; * 配置 crontab(每 10 分钟):拉快递 100 + 按履约「已发货」核对释放诊单医助(跳过已完成/已签收业务单,与是否甘草单无关) * 0,10,20,30,40,50 * * * * cd /path/to/server && php think express:auto-update >> /dev/null 2>&1 * - * 已对「业务订单创建人非二中心」(或创建人为开方医生时按待释放身份判定)执行发货/签收自动释放的诊单会写入 tcm_diagnosis.shipped_non_er_assistant_cleared_at, + * 已对「待释放医助非二中心」执行发货/签收自动释放的诊单会写入 tcm_diagnosis.shipped_non_er_assistant_cleared_at, * 后续履约核对不再重复扫描(需先执行 sql/1.9.20260507/add_diagnosis_shipped_non_er_assistant_cleared_at.sql)。 */ class ExpressAutoUpdate extends Command diff --git a/server/app/command/GancaoSyncLogisticsRoute.php b/server/app/command/GancaoSyncLogisticsRoute.php index 61cc28ca..fc0c2ec8 100755 --- a/server/app/command/GancaoSyncLogisticsRoute.php +++ b/server/app/command/GancaoSyncLogisticsRoute.php @@ -34,7 +34,7 @@ use think\console\Output; * 0,30 * * * * cd /path/to/server && php think gancao:sync-logistics >> runtime/log/gancao_sync.log 2>&1 * * 跳过履约状态为已完成(3)、已签收(6) 的业务订单,不再拉取甘草路由。 - * 已对「业务订单创建人非二中心」(发货/签收自动释放规则见 ExpressTrackingService)的诊单会写入 tcm_diagnosis.shipped_non_er_assistant_cleared_at, + * 已对「待释放医助非二中心」(发货/签收自动释放规则见 ExpressTrackingService)的诊单会写入 tcm_diagnosis.shipped_non_er_assistant_cleared_at, * 后续履约核对不再重复扫描(需先执行 sql/1.9.20260507/add_diagnosis_shipped_non_er_assistant_cleared_at.sql)。 */ class GancaoSyncLogisticsRoute extends Command diff --git a/server/app/common/service/ExpressTrackingService.php b/server/app/common/service/ExpressTrackingService.php index 77da5c43..f69b63cc 100755 --- a/server/app/common/service/ExpressTrackingService.php +++ b/server/app/common/service/ExpressTrackingService.php @@ -588,7 +588,7 @@ class ExpressTrackingService /** * 处方业务订单履约「已发货(5) / 已签收(6)」时:清空关联诊单(患者)医助,并写入 tcm_diagnosis_assign_log(含关联业务订单 creator_id / create_time 快照,便于核对医助创建订单时间)。 * 快递 100 定时拉轨迹、甘草路由、甘草回调与履约核对共用同一规则。 - * 不自动清空的情形:(1)按业务订单 creator_id(代建场景)或回退身份判断是否在「二中心」部门子树;(2)诊单已有 shipped_non_er_assistant_cleared_at 且 assistant_id 已为 0(视为已处理);(3)指派日志存在后台人工指派:`operator_admin_id>0` 且 `to_assistant_id>0`(系统任务为 operator_admin_id=0);或 related_po_creator_id=from_assistant_id 且 to>0 的旧数据兼容。避免「系统·已发货履约核对」覆盖人工指派链。 + * 不自动清空的情形:(1)待释放医助(诊单 assistant_id,或代建回退身份)归属「二中心」部门子树;(2)诊单当前已指派、或指派日志曾 to 给「二中心」医助(含系统自动分配);(3)诊单已有 shipped_non_er_assistant_cleared_at 且 assistant_id 已为 0(视为已处理);(4)指派日志存在后台人工指派:`operator_admin_id>0` 且 `to_assistant_id>0`;或 related_po_creator_id=from_assistant_id 且 to>0 的旧数据兼容。避免「系统·已发货履约核对」覆盖人工指派链。 * * @param array{tracking_id?:int,tracking_number?:string,source?:string} $meta * @return array{ @@ -663,19 +663,18 @@ class ExpressTrackingService return null; } - /** 是否因「二中心」跳过释放:优先看业务订单创建人 creator_id(代建场景);创建人即开方医生时用 fromAssistantId */ - $erZhongxinCheckAdminId = ($poCreatorId > 0 && ($rxId <= 0 || $poCreatorId !== $rxCreator)) - ? $poCreatorId - : $fromAssistantId; - - if ($erZhongxinCheckAdminId > 0 && self::currentAssistantBelongsToErzhongxinDeptTree($erZhongxinCheckAdminId)) { - Log::info('Skipped auto assistant release (shipped/signed): order creator (or fallback from-id) under 二中心 dept tree', [ + // 二中心不释放:(a)待释放医助属二中心;(b)诊单当前医助属二中心;(c)指派日志曾指派给二中心医助 + $skipErzhongxin = self::currentAssistantBelongsToErzhongxinDeptTree($fromAssistantId) + || ($diagAssistantIdNow > 0 && self::currentAssistantBelongsToErzhongxinDeptTree($diagAssistantIdNow)) + || self::diagnosisHasBeenAssignedToErzhongxinAssistant($diagnosisId); + if ($skipErzhongxin) { + Log::info('Skipped auto assistant release (shipped/signed): 二中心 assistant assigned or targeted', [ 'diagnosis_id' => $diagnosisId, 'prescription_order_id' => $orderId, 'source' => $source, - 'er_zhongxin_check_admin_id' => $erZhongxinCheckAdminId, - 'prescription_order_creator_id' => $poCreatorId, - 'from_assistant_id_for_log' => $fromAssistantId, + 'from_assistant_id' => $fromAssistantId, + 'diagnosis_assistant_id' => $diagAssistantIdNow, + 'prescription_order_creator_id' => $poCreatorId, ]); return null; @@ -798,6 +797,36 @@ class ExpressTrackingService return false; } + /** + * 诊单指派日志是否曾指派给「二中心」医助(to_assistant_id>0,含人工指派与系统自动分配)。 + */ + private static function diagnosisHasBeenAssignedToErzhongxinAssistant(int $diagnosisId): bool + { + if ($diagnosisId <= 0) { + return false; + } + $toIds = Db::name('tcm_diagnosis_assign_log') + ->where('diagnosis_id', $diagnosisId) + ->where('to_assistant_id', '>', 0) + ->column('to_assistant_id'); + if ($toIds === [] || $toIds === null) { + return false; + } + $seen = []; + foreach ($toIds as $aid) { + $aid = (int) $aid; + if ($aid <= 0 || isset($seen[$aid])) { + continue; + } + $seen[$aid] = true; + if (self::currentAssistantBelongsToErzhongxinDeptTree($aid)) { + return true; + } + } + + return false; + } + /** * 若诊单曾被后台人工指派到具体医助,则不再执行发运/签收自动清空(避免「系统·已发货履约核对」覆盖人工链)。 * 判定:operator_admin_id>0 且 to_assistant_id>0(人工);系统写入的释放/同步为 operator_admin_id=0。 @@ -1031,7 +1060,7 @@ class ExpressTrackingService /** * 按处方订单履约核对:仅已发货(5) 时符合条件的订单调用 applyAssistantReleaseForShippedPrescriptionOrder(已完成/已签收不再扫描)。 - * 与快递 100 / 甘草来自哪个物流渠道无关;是否清空医助以该方法为准(**二中心豁免看业务单创建人**参见 apply;已 shipped 释放且 assistant_id=0 视为已处理)。 + * 与快递 100 / 甘草来自哪个物流渠道无关;是否清空医助以该方法为准(**二中心豁免看待释放医助**参见 apply;已 shipped 释放且 assistant_id=0 视为已处理)。 * * @return array{scanned: int, cleared: int, lines: list} */