Финальная стабильная реализация

This commit is contained in:
mi
2026-08-21 11:48:24 +03:00
parent 6e24278b10
commit d2415fcfeb
17 changed files with 662 additions and 57 deletions
+170
View File
@@ -77,6 +77,9 @@
30. #INFRASTRUCTURE перевести взаимодействие с signoz на TLS (сейчас OTEL_REMOTE_TLS_INSECURE=true)
31. #BACK_BUSINESS VM1 -> VM2: `curl -sS --cacert /etc/han/ca/vm2-internal-ca.crt "https://processing.internal:8443/internal/safety/status" | python3 -m json.tool` В коде захардкожен Redis в components = "degraded". Надо реализовать реальную проверку вместо костыля.
32. #BACK_BUSINESS Решить проблему с обновлением сигнатур CLAMAV.
33. #BACK_BUSINESS Создать поле "Номер телефона в приложении". Подробности реализации ниже (на подумать)
34. #BACK_BUSINESS реализация смены номера телефона. Подробности реализации ниже (на подумать)
35. #BACK_BUSINESS схлапывание контактов. Подробности реализации ниже (на подумать)
# Критично для релиза:
~~1. Разработка message-safety~~
@@ -85,3 +88,170 @@
~~4. Подключить OTLP-провайдер~~
~~5. Починить UI баги~~
6. Второй контур для продакшн
33. details
Комплексный алгоритм:
1. Нормализовать номер приложения `P`.
2. Если active mapping существует:
- Contact принадлежит текущему `user_id` — обновить служебные поля и app-телефон;
- `user_id` пуст — восстановить служебные поля;
- указан чужой `user_id` — ничего не перезаписывать, создать alert `mapping_identity_mismatch`;
- Contact отсутствует — пометить mapping broken и создать alert.
3. Если mapping отсутствует, собрать объединённый список Contact:
- найденные по текущему `user_id`;
- найденные по новому полю app-телефона `P`;
- найденные через `duplicate.findbycomm` по стандартному `PHONE`.
4. Загрузить все карточки и классифицировать:
- принадлежат текущему пользователю;
- свободны — `user_id` пуст;
- принадлежат другому пользователю;
- app-телефон равен `P`;
- app-телефон пуст — legacy-контакт;
- app-телефон отличается от `P`.
Дальнейшие ветки:
- Найден Contact с тем же `user_id`:
- выбрать самый новый среди таких Contact;
- восстановить mapping;
- записать `P` в app-телефон;
- если таких Contact несколько или есть другие совпадения — создать duplicate alert со всеми ID.
- Контактов с тем же `user_id` нет:
- кандидатами считаются Contact с app-телефоном `P`;
- также допускаются legacy-контакты с пустым app-телефоном, найденные по стандартному `PHONE`;
- Contact с другим непустым app-телефоном нельзя автоматически привязывать.
- Один кандидат свободен:
- записать `user_id`, registration flag и app-телефон;
- создать mapping.
- Несколько кандидатов:
- выбрать самый новый по `CREATED_TIME`, затем по числовому ID;
- если выбранный свободен — привязать его и создать `duplicate_contacts`;
- если выбранный принадлежит другому пользователю — создать новый Contact и alert `contact_owned_by_other_user`;
- в `contactIds` alert передать все старые ID и ID нового Contact.
- Совпадения есть только по стандартному `PHONE`, но у них указан другой app-телефон:
- не считать их Contact текущего пользователя;
- создать новый Contact;
- создать alert о совпадении общего номера.
- Совпадений нет:
- создать новый Contact без alert.
Дополнительные правила:
- app-телефон — App-master: изменения этого поля в Б24 не меняют номер авторизации.
- При смене номера приложение обновляет app-телефон и гарантирует наличие нового номера в стандартном `PHONE`, но не удаляет остальные номера Contact.
- Перед повторным `contact.add` после timeout необходимо повторно искать по `user_id` и app-телефону, чтобы не создать дубль.
- Mapping создаётся только после успешной записи служебных полей.
- Все конфликтные ветки должны иметь отдельные регрессионные тесты до реализации.
34. details
Изменение стандартного `PHONE` в Битрикс24 не должно автоматически менять номер авторизации в приложении. Иначе сотрудник с доступом к CRM сможет случайно или намеренно передать чужой аккаунт другому номеру.
Рекомендуемый процесс:
1. Сотрудник меняет стандартный номер в карточке.
2. Webhook фиксирует расхождение с полем «Номер телефона в приложении».
3. Система создаёт отдельный запрос/смарт-процесс «Смена номера приложения».
4. Сотрудник явно выбирает, какой из номеров Contact является новым номером приложения.
5. Выполняются проверки:
- нормализация E.164;
- новый номер не занят другим пользователем;
- Contact действительно связан с нужным `user_id`;
- нет другого активного запроса.
6. Клиент подтверждает смену:
- OTP на новый номер;
- подтверждение в активной сессии приложения;
- желательно OTP на старый номер, если он доступен.
7. Если старый номер недоступен — отдельный усиленный сценарий идентификации сотрудником с обязательным аудитом и, желательно, дополнительным согласованием.
8. После подтверждения App меняет номер в своём источнике истины — БД приложения/Keycloak.
9. Через outbox создаётся задача синхронизации.
10. Bitrix Sync обновляет:
- поле «Номер телефона в приложении»;
- служебные поля связи;
- при необходимости добавляет новый номер в стандартный `PHONE`;
- закрывает запрос на смену номера.
Важные правила:
- Обычное редактирование `PHONE` только создаёт предложение на смену, но не меняет логин.
- Поле «Номер телефона в приложении» остаётся read-only для сотрудников.
- Старые дополнительные номера Contact автоматически не удаляются.
- Нужны срок действия запроса, ограничение попыток OTP, журнал сотрудника и времени операции.
- Повторный запрос должен быть идемпотентным.
Так сохраняется удобство работы через Б24, но CRM не становится небезопасным источником истины для номера авторизации.
35. details
Здесь нужно объединять не только карточки Битрикс24, но прежде всего два аккаунта приложения. Простое слияние Contact оставит данные личного кабинета привязанными к старому `user_id`.
Рекомендуемый сценарий account merge.
1. Создать заявку на объединение:
- старый `user_id` и старый Contact;
- новый `user_id` и новый Contact;
- результат комплайенса;
- оператор и подтверждающий сотрудник;
- причина и audit trail.
2. Выбрать канонический аккаунт. Обычно это старый аккаунт, содержащий историю и данные. Новый пустой аккаунт становится поглощаемым.
3. На время операции заблокировать изменения обоих аккаунтов и повторные merge-заявки.
4. Проверить конфликты:
- заказы, документы, согласия;
- бонусы, баланс и другие финансовые данные;
- активные процессы;
- наличие данных в новом аккаунте;
- отсутствие третьего аккаунта с новым номером.
5. На стороне приложения:
- привязать новый номер к каноническому старому аккаунту;
- старый номер сделать неактивным либо историческим;
- перенести допустимые данные нового аккаунта;
- отметить новый `user_id` как `merged_into=<canonical_user_id>`;
- запретить дальнейшее использование поглощённого аккаунта;
- обновить Keycloak/механизм авторизации;
- отозвать старые сессии и потребовать повторный вход.
6. Только после успешного App merge обработать Битрикс:
- старый Contact выбрать каноническим;
- записать в него канонический `user_id`;
- поле «Номер телефона в приложении» установить в новый номер;
- объединить стандартные номера обоих Contact;
- создать active mapping канонического пользователя на канонический Contact;
- у второго Contact очистить app-телефон, `user_id` и registration flag;
- затем объединить его с каноническим Contact штатным механизмом Б24 либо архивировать как поглощённый.
7. Закрыть старые mapping со специальной причиной `account_merge`, но сохранить историю.
8. Завершить заявку только после проверок:
- вход по новому номеру открывает старый личный кабинет;
- существует один active App user;
- существует один active mapping;
- только один Contact содержит app-телефон и registration flag;
- поглощённый аккаунт больше не создаёт задачи синхронизации.
Ключевой принцип: сначала объединение аккаунтов приложения, затем CRM. Если сначала слить карточки Б24, это не вернёт пользователю данные.
Для устойчивой реализации потребуется отдельная audited-процедура наподобие:
```text
request_account_merge(
canonical_user_id,
absorbed_user_id,
canonical_contact_id,
absorbed_contact_id,
reason,
operator_id,
compliance_case_id
)
```
Операция должна быть идемпотентной, поэтапной и восстанавливаемой после сбоя. Для финансовых или юридически значимых данных автоматический перенос без отдельных правил недопустим.