model = new EctActions(); } public static function new(): static { return new self(); } /** * 从数据库查询最大的批次号 * 用于分布式场景下保证批次号一致性 * * @param string $machineId * @return string|null 批次号,未找到返回 null */ public function findTodayActiveBatchNo(string $machineId): ?string { try { // 1. 精准限定今日时间范围(避免跨天数据) $todayStart = date('Y-m-d 00:00:00'); $todayEnd = date('Y-m-d 23:59:59'); $todayDate = date('Ymd'); $record = EctActions::where('process_name', '<>', '结束') ->whereBetween('created_at', [$todayStart, $todayEnd]) ->where('op_batchno', 'like', $todayDate . $machineId . '%') // 匹配今日日期+指定机器ID开头的批次号 ->select('op_batchno') ->orderBy('op_batchno', 'desc') // 降序排列,最大的序列号(批次号)排在最前 ->lockForUpdate() ->first(); // 获取第一条(即序列号最大的)记录 // 3. 严谨的空值判断 if (empty($record) || empty(trim($record->op_batchno))) { return null; } return (string)trim($record->op_batchno); } catch (\Exception $e) { // 记录异常(可选:根据项目日志规范调整) Logger::error('查询最大批次号失败:', [ 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); return null; } } /** * 查询内镜最后一条洗消操作记录 * * @param string $endoscopeId 内镜ID * @param array $excludeActionTypes 排除的操作类型(如 [0, 7, 8]) * @return EctActions|null */ public function findLastAction(string $endoscopeId, array $excludeActionTypes = []): ?EctActions { $query = EctActions::where('endoscope_id', $endoscopeId); if (!empty($excludeActionTypes)) { $query->whereNotIn('action_type', $excludeActionTypes); } /** @var EctActions|null */ return $query->orderBy('action_id', 'desc')->first(); } /** * 查询今日洗消记录数 * * @param string $endoscopeId 内镜ID * @param array $excludeActionTypes 排除的操作类型 0诊疗 1手工洗 2机洗 3 手工洗(晨洗)4机洗(晨洗)5手工洗(加强)6机洗(加强)7测漏 8存储 * @return int */ public function countTodayActions(string $endoscopeId, string $todayStart, array $excludeActionTypes = []): int { $todayStart = date('Y-m-d H:i:s', strtotime($todayStart)); $query = EctActions::where('endoscope_id', $endoscopeId) ->where('created_at', '>=', $todayStart); if (!empty($excludeActionTypes)) { $query->whereNotIn('action_type', $excludeActionTypes); } // 遍历 $count = 0; $today = date('Ymd'); $query->lazy()->each(function (EctActions $record) use (&$count, $today) { $batchInfo = ProcessContext::parseBatchNo($record->op_batchno); $dateTime = strtotime($batchInfo['date']); if (empty($batchInfo['date']) || $dateTime === false) return; $recordDate = date('Ymd', $dateTime); if ($recordDate >= $today) { $count += 1; } }); return $count; } /** * 查询最后一次存储入库时间 * * @param string $endoscopeId 内镜ID * @return string|null op_starttime,未找到返回 null */ public function findLastStorageTime(string $endoscopeId): ?string { $record = EctActions::where('endoscope_id', $endoscopeId) ->where('action_type', 8) ->where('process_name', '内镜放入') ->select('op_starttime') ->orderBy('action_id', 'desc') ->first(); if ($record === null) { return null; } return (string)$record->op_starttime; } /** * 查询内镜当前是否在存储柜中 * 根据最后一次存储操作判断:入库则在库中,出库则不在 * * @param string $endoscopeId 内镜ID * @return bool true=在库中,false=不在库中 */ public function isEndoscopeInStorage(string $endoscopeId): bool { $record = EctActions::where('endoscope_id', $endoscopeId) ->where('action_type', 8) ->whereIn('process_name', ['内镜放入', '内镜取出']) ->select('process_name', 'op_starttime') ->orderBy('action_id', 'desc') ->first(); if ($record === null) { return false; } return $record->process_name === '内镜放入'; } /** * 查询最后一次存储操作记录 * * @param string $endoscopeId 内镜ID * @return array|null ['process_name' => ..., 'op_starttime' => ...] 或 null */ public function findLastStorageAction(string $endoscopeId): ?array { $record = EctActions::where('endoscope_id', $endoscopeId) ->where('action_type', 8) ->whereIn('process_name', ['内镜放入', '内镜取出']) ->select('process_name', 'op_starttime', 'action_id') ->orderBy('action_id', 'desc') ->first(); if ($record === null) { return null; } return [ 'process_name' => (string)$record->process_name, 'op_starttime' => (string)$record->op_starttime, 'action_id' => (int)$record->action_id, ]; } /** * 查询批次号对应的操作员信息 * * @param string $batchNo 批次号 * @return array|null [字段名=> 值, ...] 或 null */ public function findOperatorByBatchNo(string $batchNo): ?array { $record = EctActions::where('op_batchno', $batchNo) ->whereNotNull('opuser_id') ->select('opuser_id', 'opuser_name', 'opuser_rfid', 'opuser_type') ->first(); if ($record === null) { return null; } return [ 'id' => (string)$record->opuser_id, 'name' => (string)$record->opuser_name, 'rfid' => (string)$record->opuser_rfid, 'type' => (int)$record->opuser_type, ]; } /** * 插入一条洗消记录 * * @param array $data 字段数组 * @return EctActions */ public function insert(array $data): EctActions { return EctActions::create($data); } /** * 从 ProcessContext 保存洗消记录 * * @param ProcessContext $context 流程上下文 * @param int $opuserType 操作员类型 * @return EctActions|null */ public function saveFromContext(ProcessContext $context, int $opuserType): ?EctActions { $op_endtime = null; $process_order = 1; if ($context->currentStep === '结束') { $op_endtime = date('Y-m-d H:i:s'); } if (!empty($context->previousAction)) { $process_order = (int)($context->previousAction->process_order ?? 1); } if (in_array(DbOperationType::INSERT, $context->dbOperation)) { return $this->insert([ 'op_batchno' => $context->batchNo, 'action_type' => $this->mapActionType($context->processType), 'action_type_name' => $context->processType, 'process_name' => $context->currentStep, 'process_order' => $process_order + 1, 'op_morning' => $context->needMorningWash ? 1 : 0, 'op_enhance' => $context->needEnhanceWash ? 1 : 0, 'reader_id' => (int)$context->readerId ?: null, 'reader_no' => $context->readerNo ?: null, 'opuser_type' => $opuserType, 'opuser_id' => (int)$context->operatorId ?: null, 'opuser_rfid' => $context->operatorRfid ?: null, 'opuser_name' => $context->operatorName ?: null, 'endoscope_id' => (int)$context->endoscopeId ?: null, 'endoscope_rfid' => $context->cardNo ?: null, 'endoscope_name' => $context->endoscopeName ?: null, 'op_starttime' => $context->actionStartTime ?: date('Y-m-d H:i:s'), 'op_endtime' => $op_endtime, 'op_duration' => $context->duration ?: null, 'created_at' => date('Y-m-d H:i:s'), ]); } elseif (in_array(DbOperationType::UPDATE, $context->dbOperation)) { EctActions::where('op_batchno', $context->batchNo) ->where('endoscope_id', $context->endoscopeId) ->update([ 'action_type' => $this->mapActionType($context->processType), 'action_type_name' => $context->processType, ]); } return null; } /** * 映射流程类型到 action_type * 0诊疗 1手工洗 2机洗 3 手工洗(晨洗)4机洗(晨洗)5手工洗(加强)6机洗(加强) */ protected function mapActionType(string $processType): int { $mapping = [ '诊疗' => 0, '手工洗' => 1, '手工洗(晨洗)' => 3, '手工洗(加强)' => 5, '机洗' => 2, '机洗(晨洗)' => 4, '机洗(加强)' => 6, '测漏' => 7, '存储' => 8, ]; return $mapping[$processType] ?? 1; } /** * 更新批次号最后一条记录的结束时间和时长 * * @param string $batchNo 批次号 * @param string $endTime 结束时间 * @param int $duration 时长(秒) * @return int 影响行数 */ public function updateEndTime(string $batchNo, string $endTime, int $duration): int { return EctActions::where('op_batchno', $batchNo) ->orderBy('action_id', 'desc') ->limit(1) ->update([ 'op_endtime' => $endTime, 'op_duration' => $duration, ]); } }