$ids 自动分配日志 id * @return array{success:int,failed:int,messages:list}|false */ public static function rollback(array $ids, int $adminId, array $adminInfo = []): array|false { $idList = array_values(array_unique(array_filter(array_map('intval', $ids), static fn (int $id) => $id > 0))); if ($idList === []) { self::setError('请选择要回退的记录'); return false; } $adminName = (string) ($adminInfo['name'] ?? ''); $adminAccount = (string) ($adminInfo['account'] ?? ''); $req = request(); $ip = (string) ($req->ip() ?? ''); $now = time(); $success = 0; $failed = 0; $messages = []; foreach ($idList as $logId) { try { $ret = self::rollbackOne($logId, $adminId, $adminName, $adminAccount, $ip, $now); if ($ret === true) { $success++; } else { $failed++; $messages[] = (string) $ret; } } catch (\Throwable $e) { $failed++; $messages[] = sprintf('日志#%d:%s', $logId, $e->getMessage()); Log::warning('auto assign rollback failed: ' . $e->getMessage(), ['log_id' => $logId]); } } if ($success === 0 && $failed > 0) { self::setError($messages[0] ?? '回退失败'); return false; } return [ 'success' => $success, 'failed' => $failed, 'messages' => $messages, ]; } /** * @return true|string true=成功,string=失败原因 */ private static function rollbackOne( int $logId, int $adminId, string $adminName, string $adminAccount, string $ip, int $now ): bool|string { Db::startTrans(); try { $log = Db::name('tcm_diagnosis_auto_assign_log') ->where('id', $logId) ->lock(true) ->find(); if ($log === null || $log === []) { Db::rollback(); return sprintf('日志#%d:记录不存在', $logId); } if ((int) ($log['action'] ?? 0) !== 1) { Db::rollback(); return sprintf('日志#%d:仅「已分配」记录可回退', $logId); } if ((int) ($log['rollback_time'] ?? 0) > 0) { Db::rollback(); return sprintf('日志#%d:已回退,勿重复操作', $logId); } $diagnosisId = (int) ($log['diagnosis_id'] ?? 0); $assignedAssistantId = (int) ($log['assistant_id'] ?? 0); if ($diagnosisId <= 0 || $assignedAssistantId <= 0) { Db::rollback(); return sprintf('日志#%d:数据不完整,无法回退', $logId); } $diag = Db::name('tcm_diagnosis') ->where('id', $diagnosisId) ->whereNull('delete_time') ->lock(true) ->field(['id', 'assistant_id']) ->find(); if ($diag === null || $diag === []) { Db::rollback(); return sprintf('日志#%d:诊单#%d 不存在', $logId, $diagnosisId); } $currentAssistantId = (int) ($diag['assistant_id'] ?? 0); if ($currentAssistantId !== $assignedAssistantId) { Db::rollback(); return sprintf( '日志#%d:诊单#%d 当前医助已变更(非自动分配的医助),跳过回退', $logId, $diagnosisId ); } $prevAssistantId = self::resolvePreviousAssistantIdFromAutoAssign( $diagnosisId, $assignedAssistantId, (int) ($log['create_time'] ?? 0) ); $poSnap = Db::name('tcm_prescription_order') ->where('diagnosis_id', $diagnosisId) ->whereNull('delete_time') ->order(['create_time' => 'desc', 'id' => 'desc']) ->field(['creator_id', 'create_time']) ->find(); $relatedPoCreatorId = (int) ($poSnap['creator_id'] ?? 0); $relatedPoCreateTime = (int) ($poSnap['create_time'] ?? 0); if ($relatedPoCreateTime <= 0) { $relatedPoCreateTime = $now; $relatedPoCreatorId = 0; } Db::name('tcm_diagnosis') ->where('id', $diagnosisId) ->whereNull('delete_time') ->update([ 'assistant_id' => $prevAssistantId, 'assign_read_at' => $prevAssistantId > 0 ? null : 0, ]); Db::name('tcm_diagnosis_assign_log')->insert([ 'diagnosis_id' => $diagnosisId, 'from_assistant_id' => $currentAssistantId, 'to_assistant_id' => $prevAssistantId, 'operator_admin_id' => $adminId, 'operator_name' => $adminName !== '' ? $adminName : '回退自动分配', 'operator_account' => $adminAccount, 'ip' => $ip, 'related_po_creator_id' => $relatedPoCreatorId, 'related_po_create_time' => $relatedPoCreateTime, 'is_inherit' => 0, 'create_time' => $now, ]); Db::name('tcm_diagnosis_auto_assign_log') ->where('id', $logId) ->update([ 'rollback_time' => $now, 'rollback_admin_id' => $adminId, 'rollback_admin_name' => $adminName, ]); Db::commit(); return true; } catch (\Throwable $e) { Db::rollback(); throw $e; } } /** * 从「系统自动分配」指派日志取 from_assistant_id 作为回退目标。 */ private static function resolvePreviousAssistantIdFromAutoAssign( int $diagnosisId, int $assignedAssistantId, int $autoLogCreateTime ): int { $query = Db::name('tcm_diagnosis_assign_log') ->where('diagnosis_id', $diagnosisId) ->where('to_assistant_id', $assignedAssistantId) ->where('operator_name', '系统自动分配'); if ($autoLogCreateTime > 0) { $query->where('create_time', '>=', $autoLogCreateTime - 30) ->where('create_time', '<=', $autoLogCreateTime + 30); } $row = $query->order('id', 'desc')->field(['from_assistant_id'])->find(); if ($row !== null && $row !== []) { return (int) ($row['from_assistant_id'] ?? 0); } // 兜底:不限时间窗再查最近一条系统自动分配 $fallback = Db::name('tcm_diagnosis_assign_log') ->where('diagnosis_id', $diagnosisId) ->where('to_assistant_id', $assignedAssistantId) ->where('operator_name', '系统自动分配') ->order('id', 'desc') ->field(['from_assistant_id']) ->find(); return (int) ($fallback['from_assistant_id'] ?? 0); } }