Перейти к содержимому

Чистка учётных записей - неактивные, дубли, обезличивание

Убираем мусор из базы пользователей: неактивные записи, дубли, обезличивание тех, у кого есть заказы, и аккуратное удаление остальных.

Что нужно знать заранее

Удаление записи с заказами ломает историю продаж. Заказ теряет покупателя, отчёты за прошлые периоды меняются, а бухгалтерия задаёт вопросы, на которые никто уже не ответит.

Поэтому чистка учётных записей на сайте всегда делится на две принципиально разные части. Записи без следов деятельности удаляют, записи с заказами обезличивают: имя, почта и телефон заменяются, а связи с документами остаются.

Обезличивание - не то же самое, что удаление по запросу человека. Второе выполняется по обращению субъекта данных, и его состав согласуют с юристом, а не придумывают в коде.

Шаги

  1. Договориться с заказчиком, что именно считается мусором: срок, входы, заказы, подписки.
  2. Собрать список кандидатов отдельной выборкой и посчитать их число до любых правок.
  3. Развести кандидатов на две разные группы: кого удалять совсем, а кого обезличивать.
  4. Выполнить чистку порциями, записывая в журнал каждое выполненное над записью действие.
  5. Повторять чистку по расписанию небольшими порциями, а не раз в три года авралом.

Решение

Ищем записи без следов деятельности:

$rs = \Bitrix\Main\UserTable::getList(['filter' => [
'=LAST_LOGIN' => null, '<DATE_REGISTER' => (new \Bitrix\Main\Type\DateTime())->add('-2 years'),
'=ACTIVE' => 'Y'], 'select' => ['ID', 'LOGIN', 'EMAIL', 'DATE_REGISTER'], 'limit' => 500]);
$candidates = $rs->fetchAll();
printf("кандидатов: %d\n", count($candidates));

Ни одного входа за два года - разумный первый признак. Дальше его уточняют: отсутствие заказов, подписок и заявок делает запись безусловным кандидатом на удаление.

Отсеиваем тех, у кого есть заказы:

$withOrders = array_column(\Bitrix\Sale\Internals\OrderTable::getList([
'filter' => ['@USER_ID' => array_column($candidates, 'ID')],
'select' => ['USER_ID'], 'group' => ['USER_ID']])->fetchAll(), 'USER_ID');
$toDelete = array_diff(array_column($candidates, 'ID'), $withOrders);
$toAnonymize = $withOrders; // этих не удаляем, а обезличиваем

Ищем дубли по почте:

SELECT EMAIL, COUNT(*) cnt, GROUP_CONCAT(ID) ids
FROM b_user WHERE EMAIL <> '' GROUP BY EMAIL HAVING cnt > 1 ORDER BY cnt DESC LIMIT 50;
-- дубли обычно появляются при импортах и при входе через внешние сервисы

Дубли разводят вручную или по правилу. Правило простое: живой считается запись с последним входом или с заказами, остальные обезличивают и выключают.

Обезличиваем запись с заказами:

$cuser = new \CUser();
$cuser->Update($id, [
'NAME' => 'Удалённый', 'LAST_NAME' => 'пользователь', 'SECOND_NAME' => '',
'EMAIL' => 'deleted-' . $id . '@example.invalid',
'PERSONAL_PHONE' => '', 'PERSONAL_BIRTHDAY' => '', 'ACTIVE' => 'N',
]);
// заказы, оплаты и документы остаются на месте вместе со своими номерами

Удаляем записи без следов деятельности:

foreach (array_slice($toDelete, 0, 200) as $id) {
\Bitrix\Main\Diag\Debug::writeToFile(['удаление' => $id], date('H:i:s'), 'users-clean.log');
\CUser::Delete($id); // порция за запуск, журнал обязателен
}

Журнал чистки хранят дольше самой чистки. Через полгода вопрос «куда делся этот пользователь» задаст либо заказчик, либо проверяющий, и ответ должен находиться за минуту.

Типичные проблемы

После чистки часть заказов осталась без покупателя.

Удалялись подряд записи, у которых были заказы, без всякого предварительного отбора. Записи с заказами обезличивают, а удаляют только те, где следов деятельности нет.

Чистка удалила активных покупателей.

Признаком мусора считалось одно лишь отсутствие входов, без учёта заказов и подписок. Критерий чистки согласуют с заказчиком и проверяют на выборке до удаления.

Скрипт чистки падает по времени на большой базе.

Удаление идёт одним сплошным проходом сразу по всем десяткам тысяч учётных записей. Чистку ведут порциями по паре сотен записей с обязательным сохранением места остановки.

Дубли появляются снова через месяц.

Причина дублей не устранена: импорт или внешний вход заводят новую запись вместо поиска существующей. Чистка без исправления самого источника дублей рано или поздно оказывается работой впустую.

На запрос об удалении данных нечего показать.

Чистка выполнялась без журнала прохода, и подтвердить факт удаления теперь нечем. Журнал каждого действия чистки хранят заметно дольше, чем сами удалённые записи.

Частые вопросы

Что считать неактивной записью?

Обычно это отсутствие входов и заказов за согласованный срок. Точный критерий определяет заказчик, а разработчик проверяет его на выборке до чистки.

Удалять или обезличивать?

Записи без следов деятельности удаляют, записи с заказами обезличивают. Второе сохраняет документы и отчёты, а персональные данные при этом исчезают.

Что делать с дублями?

Оставить живую запись с последними входами или заказами, остальные обезличить и выключить. Заодно устранить причину: импорт или внешний вход, заводящий новые записи.

Как часто чистить базу?

По расписанию, хотя бы раз в квартал, небольшими порциями. Разовая чистка через три года превращается в отдельный проект с большим риском.

Нужно ли согласовывать чистку?

Да, и критерий, и состав обезличивания. Это решение владельца данных, а не разработчика, и его фиксируют письменно.

Смежное

Первоисточник