diff --git a/server/app/adminapi/logic/stats/AutoAssignLogLogic.php b/server/app/adminapi/logic/stats/AutoAssignLogLogic.php new file mode 100644 index 00000000..12c953af --- /dev/null +++ b/server/app/adminapi/logic/stats/AutoAssignLogLogic.php @@ -0,0 +1,227 @@ + $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); + } +} diff --git a/server/sql/1.9.20260717/add_auto_assign_log_rollback.sql b/server/sql/1.9.20260717/add_auto_assign_log_rollback.sql new file mode 100644 index 00000000..0ff220ad --- /dev/null +++ b/server/sql/1.9.20260717/add_auto_assign_log_rollback.sql @@ -0,0 +1,20 @@ +-- 自动分配日志:支持回退(撤回已自动分配的医助至上一任/一中心原医助) +-- 表:zyt_tcm_diagnosis_auto_assign_log +-- 接口权限:stats.autoAssignLog/rollback + +ALTER TABLE `zyt_tcm_diagnosis_auto_assign_log` + ADD COLUMN `rollback_time` int unsigned NOT NULL DEFAULT 0 COMMENT '回退时间戳,0=未回退' AFTER `create_time`, + ADD COLUMN `rollback_admin_id` int unsigned NOT NULL DEFAULT 0 COMMENT '回退操作人管理员ID' AFTER `rollback_time`, + ADD COLUMN `rollback_admin_name` varchar(64) NOT NULL DEFAULT '' COMMENT '回退操作人姓名' AFTER `rollback_admin_id`; + +SET @menu_id = (SELECT id FROM zyt_system_menu WHERE perms = 'stats.autoAssignLog/lists' LIMIT 1); + +INSERT INTO `zyt_system_menu` + (`pid`, `type`, `name`, `icon`, `sort`, `perms`, `paths`, `component`, `selected`, `params`, `is_show`, `is_disable`, `create_time`, `update_time`) +SELECT + @menu_id, 'A', '回退自动分配', '', 1, + 'stats.autoAssignLog/rollback', '', '', + '', '', 1, 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP() +FROM DUAL +WHERE @menu_id IS NOT NULL + AND NOT EXISTS (SELECT 1 FROM zyt_system_menu WHERE perms = 'stats.autoAssignLog/rollback');