hasTable('nuqloud_scrum_workspaces')) { $table = $schema->createTable('nuqloud_scrum_workspaces'); $this->addId($table); $table->addColumn('talk_token', Types::STRING, ['notnull' => true, 'length' => 128]); $table->addColumn('talk_room_name', Types::STRING, ['notnull' => true, 'length' => 255]); $table->addColumn('talk_room_type', Types::STRING, ['notnull' => false, 'length' => 64]); $table->addColumn('deck_board_id', Types::BIGINT, ['notnull' => false]); $table->addColumn('root_folder_path', Types::STRING, ['notnull' => true, 'length' => 1024]); $table->addColumn('sync_enabled', Types::SMALLINT, ['notnull' => true, 'default' => 1]); $table->addColumn('last_message_id', Types::BIGINT, ['notnull' => true, 'default' => 0]); $table->addColumn('last_share_message_id', Types::BIGINT, ['notnull' => true, 'default' => 0]); $table->addColumn('created_by', Types::STRING, ['notnull' => false, 'length' => 128]); $this->addTimestamps($table); $table->addUniqueIndex(['talk_token'], 'nqs_ws_token'); $table->addIndex(['sync_enabled'], 'nqs_ws_enabled'); $table->addIndex(['updated_at'], 'nqs_ws_updated'); $changed = true; } if (!$schema->hasTable('nuqloud_scrum_threads')) { $table = $schema->createTable('nuqloud_scrum_threads'); $this->addId($table); $table->addColumn('workspace_id', Types::INTEGER, ['notnull' => true]); $table->addColumn('talk_thread_id', Types::STRING, ['notnull' => false, 'length' => 128]); $table->addColumn('talk_root_message_id', Types::BIGINT, ['notnull' => true]); $table->addColumn('title', Types::STRING, ['notnull' => true, 'length' => 255]); $table->addColumn('slug', Types::STRING, ['notnull' => true, 'length' => 255]); $table->addColumn('folder_path', Types::STRING, ['notnull' => true, 'length' => 1024]); $table->addColumn('deck_card_id', Types::BIGINT, ['notnull' => false]); $table->addColumn('deck_stack_id', Types::BIGINT, ['notnull' => false]); $table->addColumn('status', Types::STRING, ['notnull' => true, 'length' => 32, 'default' => 'inbox']); $table->addColumn('priority', Types::STRING, ['notnull' => true, 'length' => 32, 'default' => 'normal']); $table->addColumn('created_by', Types::STRING, ['notnull' => false, 'length' => 128]); $table->addColumn('assigned_to', Types::STRING, ['notnull' => false, 'length' => 128]); $table->addColumn('due_at', Types::BIGINT, ['notnull' => false]); $table->addColumn('last_activity_message_id', Types::BIGINT, ['notnull' => true, 'default' => 0]); $this->addTimestamps($table); $table->addUniqueIndex(['workspace_id', 'talk_root_message_id'], 'nqs_thr_root'); $table->addUniqueIndex(['workspace_id', 'talk_thread_id'], 'nqs_thr_thread'); $table->addIndex(['workspace_id', 'status'], 'nqs_thr_status'); $table->addIndex(['deck_card_id'], 'nqs_thr_deck'); $changed = true; } if (!$schema->hasTable('nuqloud_scrum_thread_messages')) { $table = $schema->createTable('nuqloud_scrum_thread_messages'); $this->addId($table); $table->addColumn('thread_id', Types::INTEGER, ['notnull' => true]); $table->addColumn('talk_message_id', Types::BIGINT, ['notnull' => true]); $table->addColumn('actor_type', Types::STRING, ['notnull' => true, 'length' => 64]); $table->addColumn('actor_id', Types::STRING, ['notnull' => true, 'length' => 128]); $table->addColumn('actor_display_name', Types::STRING, ['notnull' => false, 'length' => 255]); $table->addColumn('message_text', Types::TEXT, ['notnull' => false]); $table->addColumn('message_hash', Types::STRING, ['notnull' => true, 'length' => 128]); $table->addColumn('raw_payload', Types::TEXT, ['notnull' => false]); $this->addTimestamps($table); $table->addUniqueIndex(['thread_id', 'talk_message_id'], 'nqs_msg_talk'); $table->addIndex(['actor_id'], 'nqs_msg_actor'); $table->addIndex(['created_at'], 'nqs_msg_created'); $changed = true; } if (!$schema->hasTable('nuqloud_scrum_files')) { $table = $schema->createTable('nuqloud_scrum_files'); $this->addId($table); $table->addColumn('workspace_id', Types::INTEGER, ['notnull' => true]); $table->addColumn('thread_id', Types::INTEGER, ['notnull' => false]); $table->addColumn('talk_message_id', Types::BIGINT, ['notnull' => false]); $table->addColumn('nc_file_id', Types::BIGINT, ['notnull' => false]); $table->addColumn('original_path', Types::STRING, ['notnull' => true, 'length' => 1024]); $table->addColumn('workspace_path', Types::STRING, ['notnull' => true, 'length' => 1024]); $table->addColumn('checksum', Types::STRING, ['notnull' => false, 'length' => 128]); $table->addColumn('size', Types::BIGINT, ['notnull' => true, 'default' => 0]); $table->addColumn('mime_type', Types::STRING, ['notnull' => false, 'length' => 255]); $table->addColumn('mirror_state', Types::STRING, ['notnull' => true, 'length' => 32, 'default' => 'pending']); $table->addColumn('sync_error', Types::TEXT, ['notnull' => false]); $this->addTimestamps($table); $table->addIndex(['workspace_id', 'thread_id'], 'nqs_file_scope'); $table->addIndex(['nc_file_id'], 'nqs_file_nc'); $table->addIndex(['mirror_state'], 'nqs_file_state'); $changed = true; } if (!$schema->hasTable('nuqloud_scrum_deck_sync')) { $table = $schema->createTable('nuqloud_scrum_deck_sync'); $this->addId($table); $table->addColumn('workspace_id', Types::INTEGER, ['notnull' => true]); $table->addColumn('thread_id', Types::INTEGER, ['notnull' => false]); $table->addColumn('deck_board_id', Types::BIGINT, ['notnull' => false]); $table->addColumn('deck_stack_id', Types::BIGINT, ['notnull' => false]); $table->addColumn('deck_card_id', Types::BIGINT, ['notnull' => false]); $table->addColumn('last_sync_at', Types::BIGINT, ['notnull' => false]); $table->addColumn('sync_state', Types::STRING, ['notnull' => true, 'length' => 32, 'default' => 'pending']); $table->addColumn('sync_error', Types::TEXT, ['notnull' => false]); $this->addTimestamps($table); $table->addIndex(['workspace_id'], 'nqs_deck_ws'); $table->addIndex(['thread_id'], 'nqs_deck_thread'); $table->addIndex(['sync_state'], 'nqs_deck_state'); $changed = true; } if (!$schema->hasTable('nuqloud_scrum_sync_cursors')) { $table = $schema->createTable('nuqloud_scrum_sync_cursors'); $this->addId($table); $table->addColumn('cursor_type', Types::STRING, ['notnull' => true, 'length' => 64]); $table->addColumn('scope_key', Types::STRING, ['notnull' => true, 'length' => 255]); $table->addColumn('cursor_value', Types::STRING, ['notnull' => true, 'length' => 255]); $table->addColumn('locked_until', Types::BIGINT, ['notnull' => false]); $table->addColumn('updated_at', Types::BIGINT, ['notnull' => true, 'default' => 0]); $table->addUniqueIndex(['cursor_type', 'scope_key'], 'nqs_cursor_scope'); $table->addIndex(['locked_until'], 'nqs_cursor_lock'); $changed = true; } return $changed ? $schema : null; } private function addId($table): void { $table->addColumn('id', Types::INTEGER, [ 'autoincrement' => true, 'notnull' => true, 'length' => 4, ]); $table->setPrimaryKey(['id']); } private function addTimestamps($table): void { $table->addColumn('created_at', Types::BIGINT, ['notnull' => true, 'default' => 0]); $table->addColumn('updated_at', Types::BIGINT, ['notnull' => true, 'default' => 0]); } }