From a141f60df0ab3835f19fd3432356040983b147aa Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Sat, 10 Jan 2026 10:17:15 +0000 Subject: [PATCH] =?UTF-8?q?Mechanizm=20obs=C5=82ugi=20proces=C3=B3w?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- WorkflowEngine.php | 338 ++++++++++++++++---------------------- _get_instance_details.php | 6 +- cookie.txt | 5 + process_definitions.php | 93 +++++++++-- test_php.php | 1 + 5 files changed, 231 insertions(+), 212 deletions(-) create mode 100644 cookie.txt create mode 100644 test_php.php diff --git a/WorkflowEngine.php b/WorkflowEngine.php index d777948..f4071b8 100644 --- a/WorkflowEngine.php +++ b/WorkflowEngine.php @@ -1,12 +1,6 @@ pdo = db(); } - /** - * Pobiera wszystkie dane niezbędne dla głównej macierzy pulpitu procesów. - * - * @return array Tablica zawierająca 'people', 'definitions' i zmapowaną tablicę 'instances'. - */ public function getDashboardMatrix(): array { // Get all people (potential assignees) $stmt_people = $this->pdo->prepare("SELECT id, firstName, lastName, companyName, role, email, phone FROM people ORDER BY lastName, firstName"); @@ -48,216 +37,179 @@ class WorkflowEngine { ]; } - /** - * Rozpoczyna nową instancję procesu dla danej osoby. - * - * @param string $processCode Unikalny kod definicji procesu. - * @param int $personId ID osoby. - * @param int $userId ID użytkownika inicjującego akcję. - * @return int|null ID nowo utworzonej instancji lub null w przypadku niepowodzenia. - */ public function startProcess(string $processCode, int $personId, int $userId): ?int { - // 1. Znajdź aktywną definicję procesu po kodzie. - // 2. Pobierz start_node_id z definicji. - // 3. Utwórz nową instancję procesu ze statusem 'in_progress' i current_node_id. - // 4. Utwórz zdarzenie systemowe dla rozpoczęcia procesu. - // TODO: Implementacja logiki. - return null; + error_log("startProcess: processCode=$processCode, personId=$personId, userId=$userId"); + $this->pdo->beginTransaction(); + try { + // 1. Find active process definition by code. + $stmt_def = $this->pdo->prepare("SELECT * FROM process_definitions WHERE code = ? AND active = 1"); + $stmt_def->execute([$processCode]); + $definition = $stmt_def->fetch(PDO::FETCH_ASSOC); + error_log("startProcess: definition=" . print_r($definition, true)); + + if (!$definition) { + throw new Exception("Active process definition with code '$processCode' not found."); + } + + $definition_json = json_decode($definition['definition_json'], true); + if (!$definition_json || !isset($definition_json['start_node_id'])) { + throw new Exception("Process definition is missing start_node_id."); + } + $startNodeId = $definition_json['start_node_id']; + + // 2. Create a new process instance. + $stmt_insert = $this->pdo->prepare( + "INSERT INTO process_instances (personId, processDefinitionId, current_node_id, current_status, lastActivityAt) VALUES (?, ?, ?, 'in_progress', NOW())" + ); + $stmt_insert->execute([$personId, $definition['id'], $startNodeId]); + error_log("startProcess: affected rows=" . $stmt_insert->rowCount()); + $instanceId = $this->pdo->lastInsertId(); + error_log("startProcess: instanceId=$instanceId"); + + // 3. Create a system event for process start. + $this->addEvent($instanceId, 'system', 'Process started.', $startNodeId, [], $userId); + + $this->pdo->commit(); + return (int)$instanceId; + } catch (Exception $e) { + $this->pdo->rollBack(); + error_log("Error in startProcess: " . $e->getMessage()); + return null; + } + } + + public function getProcessState(int $instanceId): ?array { + $stmt = $this->pdo->prepare("SELECT * FROM process_instances WHERE id = ?"); + $stmt->execute([$instanceId]); + $instance = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$instance) { + return null; + } + + $stmt_def = $this->pdo->prepare("SELECT definition_json FROM process_definitions WHERE id = ?"); + $stmt_def->execute([$instance['processDefinitionId']]); + $definition_json = $stmt_def->fetchColumn(); + $definition = json_decode($definition_json, true); + + $currentNodeId = $instance['current_node_id']; + $nodeInfo = $definition['nodes'][$currentNodeId] ?? null; + + return [ + 'instance' => $instance, + 'definition' => $definition, + 'currentNode' => $nodeInfo, + ]; + } + + public function applyTransition(int $instanceId, string $transitionId, array $inputPayload, int $userId): bool { + $this->pdo->beginTransaction(); + try { + $state = $this->getProcessState($instanceId); + if (!$state) { + throw new Exception("Process instance not found."); + } + + $instance = $state['instance']; + $definition = $state['definition']; + $currentNodeId = $instance['current_node_id']; + + // Find the transition from the definition + $transition = null; + foreach ($definition['transitions'] as $t) { + if ($t['from'] === $currentNodeId && $t['id'] === $transitionId) { + $transition = $t; + break; + } + } + + if (!$transition) { + throw new Exception("Transition not found or not allowed from the current node."); + } + + // TODO: Add rule validation here + + $newNodeId = $transition['to']; + $newNodeInfo = $definition['nodes'][$newNodeId] ?? null; + + // Update instance + $stmt_update = $this->pdo->prepare( + "UPDATE process_instances SET current_node_id = ?, current_status = ?, current_reason = ?, suggested_next_step = ?, lastActivityAt = NOW() WHERE id = ?" + ); + $stmt_update->execute([ + $newNodeId, + $newNodeInfo['ui_hints']['status'] ?? 'in_progress', + $newNodeInfo['ui_hints']['reason'] ?? '', + $newNodeInfo['ui_hints']['next_step'] ?? '', + $instanceId + ]); + + // Add event + $message = $inputPayload['message'] ?? $transition['name']; + $this->addEvent($instanceId, 'transition_applied', $message, $newNodeId, $inputPayload, $userId); + + $this->pdo->commit(); + return true; + } catch (Exception $e) { + $this->pdo->rollBack(); + error_log("Error in applyTransition: " . $e->getMessage()); + return false; + } + } + + private function addEvent(int $instanceId, string $eventType, string $message, ?string $nodeId, array $payload, int $userId): void { + $stmt = $this->pdo->prepare( + "INSERT INTO process_events (processInstanceId, eventType, message, node_id, payload_json, createdById) VALUES (?, ?, ?, ?, ?, ?)" + ); + $stmt->execute([$instanceId, $eventType, $message, $nodeId, json_encode($payload), $userId]); } - /** - * Pobiera pojedynczą instancję procesu, tworząc ją, jeśli nie istnieje. - * - * @param int $personId - * @param int $processDefinitionId - * @param int $userId Użytkownik inicjujący utworzenie, jeśli to nastąpi. - * @return array|null Tablica asocjacyjna z danymi instancji. - */ public function getOrCreateInstanceByDefId(int $personId, int $processDefinitionId, int $userId): ?array { $stmt = $this->pdo->prepare("SELECT * FROM process_instances WHERE personId = ? AND processDefinitionId = ?"); $stmt->execute([$personId, $processDefinitionId]); $instance = $stmt->fetch(PDO::FETCH_ASSOC); if (!$instance) { - // Fetch the process definition to get the initial status - $stmt_def = $this->pdo->prepare("SELECT definition_json FROM process_definitions WHERE id = ?"); + $stmt_def = $this->pdo->prepare("SELECT code FROM process_definitions WHERE id = ?"); $stmt_def->execute([$processDefinitionId]); - $definition_raw = $stmt_def->fetchColumn(); - $definition = $definition_raw ? json_decode($definition_raw, true) : null; - $initial_status = $definition['initial_status'] ?? 'none'; + $processCode = $stmt_def->fetchColumn(); - $stmt_insert = $this->pdo->prepare("INSERT INTO process_instances (personId, processDefinitionId, current_status) VALUES (?, ?, ?)"); - $stmt_insert->execute([$personId, $processDefinitionId, $initial_status]); - $instanceId = $this->pdo->lastInsertId(); - - // Utwórz zdarzenie systemowe dla utworzenia instancji - $this->addEvent($instanceId, 'system', 'Instancja utworzona.', null, [], $userId); - - // Pobierz ponownie nowo utworzoną instancję - $stmt->execute([$personId, $processDefinitionId]); - $instance = $stmt->fetch(PDO::FETCH_ASSOC); + if($processCode) { + $instanceId = $this->startProcess($processCode, $personId, $userId); + if($instanceId) { + $stmt->execute([$personId, $processDefinitionId]); + $instance = $stmt->fetch(PDO::FETCH_ASSOC); + } + } } - return $instance; + return $instance !== false ? $instance : null; } - /** - * Dodaje nowe zdarzenie do historii instancji. - * To jest wewnętrzna metoda pomocnicza. - */ - private function addEvent(int $instanceId, string $eventType, string $message, ?string $nodeId, array $payload, int $userId): void { - $stmt = $this->pdo->prepare( - "INSERT INTO process_events (processInstanceId, event_type, message, node_id, payload_json, createdById) VALUES (?, ?, ?, ?, ?, ?)" - ); - $stmt->execute([$instanceId, $eventType, $message, $nodeId, json_encode($payload), $userId]); - } - - /** - * Pobiera historię zdarzeń dla danej instancji. - * - * @param int $instanceId - * @return array - */ public function getEvents(int $instanceId): array { $stmt_events = $this->pdo->prepare("SELECT pe.*, p.email as user_email, p.firstName, p.lastName FROM process_events pe JOIN people p ON pe.createdById = p.id WHERE pe.processInstanceId = ? ORDER BY pe.createdAt DESC"); $stmt_events->execute([$instanceId]); return $stmt_events->fetchAll(PDO::FETCH_ASSOC); } - /** - * Pobiera listę dostępnych przejść z bieżącego węzła instancji. - * - * @param int $instanceId - * @return array Lista dostępnych przejść. - */ public function getAvailableTransitions(int $instanceId): array { - $stmt = $this->pdo->prepare( - 'SELECT pi.current_status, pd.definition_json ' - . 'FROM process_instances pi ' - . 'JOIN process_definitions pd ON pi.processDefinitionId = pd.id ' - . 'WHERE pi.id = ?' - ); - $stmt->execute([$instanceId]); - $result = $stmt->fetch(PDO::FETCH_ASSOC); - - if (!$result || empty($result['definition_json'])) { + $state = $this->getProcessState($instanceId); + if (!$state) { return []; } - $definition = json_decode($result['definition_json'], true); - if (!$definition || !isset($definition['transitions'])) { - return []; - } - - $current_status = $result['current_status']; - $allowed_transitions = $definition['transitions'][$current_status] ?? []; + $currentNodeId = $state['instance']['current_node_id']; + $definition = $state['definition']; $transitions = []; - foreach ($allowed_transitions as $target_status) { - $transitions[] = [ - 'id' => 'transition_' . str_replace(' ', '_', strtolower($target_status)), // e.g., transition_in_progress - 'name' => 'Oznacz jako ' . $target_status, // e.g., Oznacz jako In Progress - 'target_status' => $target_status - ]; + if (isset($definition['transitions'])) { + foreach ($definition['transitions'] as $t) { + if ($t['from'] === $currentNodeId) { + $transitions[] = $t; + } + } } return $transitions; } - - /** - * Stosuje przejście do instancji procesu. To jest główna metoda do postępu w przepływie pracy. - * - * @param int $instanceId ID instancji procesu. - * @param string $transitionId ID przejścia do zastosowania (z definition_json). - * @param array $inputPayload Dane zebrane od użytkownika dla tego kroku. - * @param int $userId ID użytkownika wykonującego akcję. - * @return bool True w przypadku sukcesu, false w przypadku porażki. - */ - public function applyTransition(int $instanceId, string $transitionId, array $inputPayload, int $userId): bool { - $this->pdo->beginTransaction(); - - try { - // 1. Pobierz instancję i dostępne przejścia - $stmt = $this->pdo->prepare("SELECT * FROM process_instances WHERE id = ?"); - $stmt->execute([$instanceId]); - $instance = $stmt->fetch(PDO::FETCH_ASSOC); - - if (!$instance) { - // Instancja nie znaleziona - $this->pdo->rollBack(); - return false; - } - - $availableTransitions = $this->getAvailableTransitions($instanceId); - - // 2. Sprawdź, czy przejście jest dozwolone - $selectedTransition = null; - if ($transitionId === 'note') { // Specjalny przypadek dodawania notatki - $selectedTransition = ['id' => 'note', 'name' => 'Dodano notatkę', 'target_status' => $instance['current_status']]; - } else { - foreach ($availableTransitions as $trans) { - if ($trans['id'] === $transitionId) { - $selectedTransition = $trans; - break; - } - } - } - - if (!$selectedTransition) { - // Nieprawidłowe lub niedozwolone przejście - $this->pdo->rollBack(); - return false; - } - - // 3. Utwórz zdarzenie - $eventType = ($transitionId === 'note') ? 'note' : 'transition_applied'; - $message = $inputPayload['message'] ?? $selectedTransition['name']; - $this->addEvent($instanceId, $eventType, $message, null, $inputPayload, $userId); - - // 4. Zaktualizuj instancję - $stmt_update = $this->pdo->prepare( - "UPDATE process_instances SET current_status = ?, lastActivityAt = CURRENT_TIMESTAMP WHERE id = ?" - ); - $stmt_update->execute([$selectedTransition['target_status'], $instanceId]); - - $this->pdo->commit(); - return true; - - } catch (Exception $e) { - $this->pdo->rollBack(); - error_log("Błąd w applyTransition: " . $e->getMessage()); - return false; - } - } - - /** - * Masowa operacja stosowania tego samego przejścia do wielu osób dla danego procesu. - * - * @param string $processCode - * @param array $personIds - * @param string $transitionId - * @param array $inputPayload - * @param int $userId - * @return array Podsumowanie wyników (np. ['success' => count, 'failed' => count]). - */ - public function bulkApplyTransition(string $processCode, array $personIds, string $transitionId, array $inputPayload, int $userId): array { - // 1. Upewnij się, że instancje istnieją dla wszystkich osób (użyj ensureInstances). - // 2. Przejdź przez pętlę personIds i wywołaj applyTransition dla każdej z nich. - // TODO: Implementacja logiki. - return ['success' => 0, 'failed' => 0]; - } - - /** - * Zapewnia, że instancje procesów istnieją dla danego zestawu osób i kodów procesów. - * Jeśli instancja brakuje, zostanie utworzona. - * - * @param array $personIds - * @param array $processCodes - * @param int $userId - * @return array Podsumowanie utworzonych instancji. - */ - public function ensureInstances(array $personIds, array $processCodes, int $userId): array { - // TODO: Implementacja logiki do tworzenia brakujących instancji. - return ['created' => 0]; - } -} \ No newline at end of file +} diff --git a/_get_instance_details.php b/_get_instance_details.php index d2938e0..b19d060 100644 --- a/_get_instance_details.php +++ b/_get_instance_details.php @@ -1,4 +1,5 @@ getOrCreateInstanceByDefId($personId, $processDefinitionId, $userId); + if (!$instance) { http_response_code(500); - die('Nie można pobrać lub utworzyć instancji procesu.'); + die("Nie można pobrać lub utworzyć instancji procesu. personId: $personId, processDefinitionId: $processDefinitionId, userId: $userId"); } $instanceId = $instance['id']; diff --git a/cookie.txt b/cookie.txt new file mode 100644 index 0000000..82d4cef --- /dev/null +++ b/cookie.txt @@ -0,0 +1,5 @@ +# Netscape HTTP Cookie File +# https://curl.se/docs/http-cookies.html +# This file was generated by libcurl! Edit at your own risk. + +localhost FALSE / FALSE 0 PHPSESSID u19ekrhqoemk4c5avca3umanfb diff --git a/process_definitions.php b/process_definitions.php index ef1b232..69d7f07 100644 --- a/process_definitions.php +++ b/process_definitions.php @@ -39,6 +39,7 @@ $processes = $stmt->fetchAll(PDO::FETCH_ASSOC);