diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..579d38b --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,120 @@ +name: Build and Publish + +on: + push: + branches: + - master + - develop + workflow_dispatch: + +permissions: + contents: write + packages: write + +jobs: + build-and-publish: + runs-on: ubuntu-latest + steps: + - name: Checkout module repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + path: module + + - name: Checkout workflows repository + uses: actions/checkout@v4 + with: + repository: mikopbx/.github-workflows + path: .github-workflows + + - name: Validate module configuration + id: validate + uses: ./.github-workflows/.github/actions/validate + + - name: Manage versions + id: version + uses: ./.github-workflows/.github/actions/versioning + with: + initial_version: "1.269" + + - name: Setup build environment + uses: ./.github-workflows/.github/actions/docker-build + with: + php_version: ${{ steps.validate.outputs.php_version }} + + - name: Handle dependencies + uses: ./.github-workflows/.github/actions/composer + with: + php_version: ${{ steps.validate.outputs.php_version }} + + - name: Cache Docker images + uses: actions/cache@v3 + id: docker-cache + with: + path: ${{ runner.temp }}/docker-cache-*.tar + key: ${{ runner.os }}-docker-${{ hashFiles('module/**/Dockerfile') }} + restore-keys: | + ${{ runner.os }}-docker- + + - name: Replace OAuth placeholders + env: + CLIENT_ID_RU: ${{ secrets.CLIENT_ID_RU }} + CLIENT_SECRET_RU: ${{ secrets.CLIENT_SECRET_RU }} + CLIENT_ID_KZ: ${{ secrets.CLIENT_ID_KZ }} + CLIENT_SECRET_KZ: ${{ secrets.CLIENT_SECRET_KZ }} + CLIENT_ID_BEL: ${{ secrets.CLIENT_ID_BEL }} + CLIENT_SECRET_BEL: ${{ secrets.CLIENT_SECRET_BEL }} + CLIENT_ID_W: ${{ secrets.CLIENT_ID_W }} + CLIENT_SECRET_W: ${{ secrets.CLIENT_SECRET_W }} + run: | + cd module + sed -i "s|-CLIENT_ID_RU-|$CLIENT_ID_RU|g" Lib/Constants.php + sed -i "s|-CLIENT_SECRET_RU-|$CLIENT_SECRET_RU|g" Lib/Constants.php + sed -i "s|-CLIENT_ID_KZ-|$CLIENT_ID_KZ|g" Lib/Constants.php + sed -i "s|-CLIENT_SECRET_KZ-|$CLIENT_SECRET_KZ|g" Lib/Constants.php + sed -i "s|-CLIENT_ID_BEL-|$CLIENT_ID_BEL|g" Lib/Constants.php + sed -i "s|-CLIENT_SECRET_BEL-|$CLIENT_SECRET_BEL|g" Lib/Constants.php + sed -i "s|-CLIENT_ID_W-|$CLIENT_ID_W|g" Lib/Constants.php + sed -i "s|-CLIENT_SECRET_W-|$CLIENT_SECRET_W|g" Lib/Constants.php + echo "Constants.php placeholders replaced." + + - name: Sync translations from Core + uses: ./.github-workflows/.github/actions/sync-translations + + - name: Create module package + id: archive + uses: ./.github-workflows/.github/actions/archive + with: + version: ${{ steps.version.outputs.new_version }} + + - name: Generate changelog + id: changelog + if: steps.validate.outputs.changelog_enabled == 'true' + uses: ./.github-workflows/.github/actions/changelog + + - name: Publish module + if: | + github.ref == 'refs/heads/master' && + steps.validate.outputs.publish_release == 'true' + env: + OWNCLOUD_AUTH: ${{ secrets.OWNCLOUD_AUTH }} + WEBDAV_ROOT: ${{ secrets.WEBDAV_ROOT }} + SHARE_API_URL: ${{ secrets.SHARE_API_URL }} + MIKO_LIC_REST_VENDOR_ID: ${{ secrets.MIKO_LIC_REST_VENDOR_ID }} + MIKO_LIC_REST_API_KEY: ${{ secrets.MIKO_LIC_REST_API_KEY }} + MIKO_LIC_HOSTNAME: ${{ secrets.MIKO_LIC_HOSTNAME }} + uses: ./.github-workflows/.github/actions/publish + with: + php_version: ${{ steps.validate.outputs.php_version }} + module_filename: ${{ steps.archive.outputs.filename }} + version: ${{ steps.version.outputs.new_version }} + changelog: ${{ steps.changelog.outputs.changelog }} + + - name: Manage GitHub releases + if: steps.validate.outputs.create_github_release == 'true' + uses: ./.github-workflows/.github/actions/release + with: + version: ${{ steps.version.outputs.new_version }} + version_tag: ${{ steps.version.outputs.new_version_tag }} + module_filename: ${{ steps.archive.outputs.filename }} + changelog: ${{ steps.changelog.outputs.changelog }} diff --git a/bin/WorkerBitrix24IntegrationHTTP.php b/bin/WorkerBitrix24IntegrationHTTP.php index 1855a94..e1b9e15 100644 --- a/bin/WorkerBitrix24IntegrationHTTP.php +++ b/bin/WorkerBitrix24IntegrationHTTP.php @@ -47,8 +47,8 @@ class WorkerBitrix24IntegrationHTTP extends WorkerBase private int $lastSyncTime = 0; private int $syncInterval = 10; - private const SYNC_INTERVAL_MIN = 10; - private const SYNC_INTERVAL_MAX = 300; + private const SYNC_INTERVAL_MIN = 60; + private const SYNC_INTERVAL_MAX = 600; private const SYNC_INTERVAL_STEP = 2; private ?int $pidLinksSyncProc = null; @@ -507,31 +507,57 @@ public function handleEvent($result):void 'ONCRMCOMPANYUPDATE' => Bitrix24Integration::API_CRM_LIST_COMPANY, ]; $events = $result['event.offline.get']['events'] ?? []; - $args = []; + + // Дедупликация: собираем уникальные ID по типу сущности. + $updateIds = []; + $contactIdsForLinks = []; + $companyIdsForLinks = []; + foreach ($events as $event) { $eventData = $event['EVENT_DATA']; if (isset($eventActionsDelete[$event['EVENT_NAME']])) { $id = array_values($eventData['FIELDS']); - ConnectorDb::invoke(ConnectorDb::FUNC_DELETE_CONTACT_DATA, [$eventActionsDelete[$event['EVENT_NAME']], $id],false); + ConnectorDb::invoke(ConnectorDb::FUNC_DELETE_CONTACT_DATA, [$eventActionsDelete[$event['EVENT_NAME']], $id], false); } if (isset($eventActionsUpdate[$event['EVENT_NAME']])){ + $type = $eventActionsUpdate[$event['EVENT_NAME']]; $arIds = array_values($eventData['FIELDS']); - $args[] = $this->b24->crmListEnt($eventActionsUpdate[$event['EVENT_NAME']], $arIds); - foreach ($arIds as $id){ - if($event['EVENT_NAME'] === 'ONCRMCONTACTUPDATE'){ - $args[] = $this->b24->getContactCompany($id); - }elseif($event['EVENT_NAME'] === 'ONCRMCOMPANYUPDATE'){ - $args[] = $this->b24->getCompanyContacts($id); + foreach ($arIds as $id) { + $updateIds[$type][$id] = true; + if ($event['EVENT_NAME'] === 'ONCRMCONTACTUPDATE') { + $contactIdsForLinks[$id] = true; + } elseif ($event['EVENT_NAME'] === 'ONCRMCOMPANYUPDATE') { + $companyIdsForLinks[$id] = true; } } } - $this->b24->handleEvent([ 'event' => $event, 'data' => $eventData]); + $this->b24->handleEvent(['event' => $event, 'data' => $eventData]); + } + + // Один crmListEnt на тип сущности вместо отдельного на каждое событие. + $args = []; + foreach ($updateIds as $type => $idsMap) { + $args[] = $this->b24->crmListEnt($type, array_keys($idsMap)); } - if(!empty($args)){ + // Relationship-запросы с кешем (TTL 5 мин) — пропускаем недавно запрошенные. + foreach (array_keys($contactIdsForLinks) as $id) { + $cacheKey = 'rel_contact_' . $id; + if ($this->b24->getCache($cacheKey) === null) { + $args[] = $this->b24->getContactCompany($id); + $this->b24->saveCache($cacheKey, '1', 300); + } + } + foreach (array_keys($companyIdsForLinks) as $id) { + $cacheKey = 'rel_company_' . $id; + if ($this->b24->getCache($cacheKey) === null) { + $args[] = $this->b24->getCompanyContacts($id); + $this->b24->saveCache($cacheKey, '1', 300); + } + } + + if (!empty($args)) { $this->q_req = array_merge($this->q_req, array_merge(...$args)); } - // События уже обрабатываются поштучно выше (crmListEnt + getContactCompany/getCompanyContacts). - // Не сбрасываем syncInterval — адаптивный backoff должен работать. }