pdo = new PDO( 'mysql:host=' . KI_DEV_DB_HOST . ';dbname=' . KI_DEV_DB_NAME . ';charset=utf8mb4', KI_DEV_DB_USER, KI_DEV_DB_PASS, [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ] ); } public function findAll(array $filters = [], int $limit = 50): array { $sql = 'SELECT * FROM contracts WHERE 1=1'; $params = []; if (isset($filters['status']) && $filters['status'] !== '') { $sql .= ' AND status = :status'; $params['status'] = $filters['status']; } if (isset($filters['search']) && $filters['search'] !== '') { $sql .= ' AND (name LIKE :search OR scope_description LIKE :search2)'; $params['search'] = '%' . $filters['search'] . '%'; $params['search2'] = '%' . $filters['search'] . '%'; } $sql .= ' ORDER BY name ASC, version DESC LIMIT :limit'; $stmt = $this->pdo->prepare($sql); foreach ($params as $key => $value) { $stmt->bindValue($key, $value); } $stmt->bindValue('limit', $limit, PDO::PARAM_INT); $stmt->execute(); return $stmt->fetchAll(PDO::FETCH_ASSOC); } public function findById(int $id): ?array { $stmt = $this->pdo->prepare('SELECT * FROM contracts WHERE id = :id'); $stmt->execute(['id' => $id]); $result = $stmt->fetch(PDO::FETCH_ASSOC); return $result ?: null; } public function findByName(string $name, ?string $version = null): ?array { if ($version !== null) { $stmt = $this->pdo->prepare( 'SELECT * FROM contracts WHERE name = :name AND version = :version' ); $stmt->execute(['name' => $name, 'version' => $version]); } else { $stmt = $this->pdo->prepare( "SELECT * FROM contracts WHERE name = :name AND status = 'active' ORDER BY version DESC LIMIT 1" ); $stmt->execute(['name' => $name]); } $result = $stmt->fetch(PDO::FETCH_ASSOC); return $result ?: null; } public function create(array $data): int { $stmt = $this->pdo->prepare(' INSERT INTO contracts (uuid, name, version, status, yaml_content, scope_description, created_by) VALUES (UUID(), :name, :version, :status, :yaml_content, :scope_description, :created_by) '); $stmt->execute([ 'name' => $data['name'], 'version' => $data['version'] ?? '1.0', 'status' => $data['status'] ?? 'active', 'yaml_content' => $data['yaml_content'], 'scope_description' => $data['scope_description'] ?? null, 'created_by' => $data['created_by'] ?? 'web', ]); return (int) $this->pdo->lastInsertId(); } public function createNewVersion(int $id, string $yamlContent, string $newVersion, string $changeDescription): void { $contract = $this->findById($id); if ($contract === null) { throw new \RuntimeException("Contract {$id} not found"); } $this->pdo->beginTransaction(); try { // Historie speichern $stmt = $this->pdo->prepare(' INSERT INTO contract_history (contract_id, previous_yaml, previous_version, change_description, changed_by) VALUES (:contract_id, :previous_yaml, :previous_version, :change_description, :changed_by) '); $stmt->execute([ 'contract_id' => $id, 'previous_yaml' => $contract['yaml_content'], 'previous_version' => $contract['version'], 'change_description' => $changeDescription, 'changed_by' => 'web', ]); // Contract aktualisieren $stmt = $this->pdo->prepare(' UPDATE contracts SET yaml_content = :yaml_content, version = :version WHERE id = :id '); $stmt->execute([ 'yaml_content' => $yamlContent, 'version' => $newVersion, 'id' => $id, ]); $this->pdo->commit(); } catch (\Exception $e) { $this->pdo->rollBack(); throw $e; } } public function deprecate(int $id): void { $stmt = $this->pdo->prepare("UPDATE contracts SET status = 'deprecated' WHERE id = :id"); $stmt->execute(['id' => $id]); } public function getHistory(int $contractId): array { $stmt = $this->pdo->prepare(' SELECT * FROM contract_history WHERE contract_id = :contract_id ORDER BY changed_at DESC '); $stmt->execute(['contract_id' => $contractId]); return $stmt->fetchAll(PDO::FETCH_ASSOC); } public function getValidations(int $contractId, int $limit = 10): array { $stmt = $this->pdo->prepare(' SELECT * FROM contract_validations WHERE contract_id = :contract_id ORDER BY validated_at DESC LIMIT :limit '); $stmt->bindValue('contract_id', $contractId, PDO::PARAM_INT); $stmt->bindValue('limit', $limit, PDO::PARAM_INT); $stmt->execute(); return $stmt->fetchAll(PDO::FETCH_ASSOC); } public function runValidation(int $id): array { $contract = $this->findById($id); if ($contract === null) { return ['success' => false, 'error' => 'Contract not found']; } // Vereinfachte Validierung - prüft ob YAML gültig ist $startTime = microtime(true); try { $data = yaml_parse($contract['yaml_content']); $result = 'passed'; $critical = 0; $major = 0; $minor = 0; $violations = []; // Basis-Prüfungen if (!isset($data['contract'])) { $major++; $violations[] = ['type' => 'major', 'message' => 'Missing contract root element']; } if (!isset($data['contract']['name'])) { $minor++; $violations[] = ['type' => 'minor', 'message' => 'Missing contract name']; } if ($critical > 0) { $result = 'failed'; } } catch (\Exception $e) { $result = 'failed'; $critical = 1; $major = 0; $minor = 0; $violations = [['type' => 'critical', 'message' => 'Invalid YAML: ' . $e->getMessage()]]; } $durationMs = (int) ((microtime(true) - $startTime) * 1000); // Validierung speichern $stmt = $this->pdo->prepare(" INSERT INTO contract_validations (contract_id, result, critical_count, major_count, minor_count, violations, triggered_by, duration_ms) VALUES (:contract_id, :result, :critical, :major, :minor, :violations, 'manual', :duration_ms) "); $stmt->execute([ 'contract_id' => $id, 'result' => $result, 'critical' => $critical, 'major' => $major, 'minor' => $minor, 'violations' => json_encode($violations), 'duration_ms' => $durationMs, ]); return [ 'success' => true, 'result' => $result, 'critical' => $critical, 'major' => $major, 'minor' => $minor, 'violations' => $violations, 'duration_ms' => $durationMs, ]; } public function getStatistics(): array { $stats = [ 'total' => 0, 'active' => 0, 'draft' => 0, 'deprecated' => 0, 'validations_total' => 0, 'validations_passed' => 0, 'validations_failed' => 0, ]; // Contracts nach Status $stmt = $this->pdo->query('SELECT status, COUNT(*) as cnt FROM contracts GROUP BY status'); foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) { $stats[$row['status']] = (int) $row['cnt']; $stats['total'] += (int) $row['cnt']; } // Validierungen $stmt = $this->pdo->query('SELECT result, COUNT(*) as cnt FROM contract_validations GROUP BY result'); foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) { $key = 'validations_' . $row['result']; $stats[$key] = (int) $row['cnt']; $stats['validations_total'] += (int) $row['cnt']; } return $stats; } }