Практически в каждой компании есть корпоративная система поощрений. Но вот как прописать для неё правила, да ещё и на Битриксе, — вопрос. Делюсь личным опытом. Привет! Я full-stack веб-разработчик в IBS, меня зовут Вячеслав Степин, и это мой дебют на Хабре. Недавно мне дали такую задачу — сделать зарабатываемые достижения для пользователей корпоративного портала компании в рамках геймификации системы поощрения сотрудников «Спасибо». На первый взгляд задача не кажется экстремально сложной, да и описанных систем лояльности уже есть немало. Но я начал изучать эту тему и столкнулся с недостаточным объёмом информации. В основном на форумах подробно расхваливают системы с точки зрения эйчаров, а вот как именно описать правила для достижений — все почему-то молчат (в лучшем случае поверхностно описывают создание основных таблиц в двух-трёх абзацах). В итоге я решил продумать всё самостоятельно и поделиться с вами опытом разработки системы. Надеюсь, кому-то эта статья поможет сэкономить время (а также силы и нервы). По крайней мере, далее будет текст, который я бы хотел прочитать сам перед тем, как браться за работу. Итак, вводные: реализовать функциональность достижений на CMS «1С-Битрикс» (на ней построен корпоративный портал). Типы достижений: ручные и автоматические. Ручные назначаются администратором портала за какие-нибудь заслуги (участие в турнирах, благотворительные инициативы и т. п.). Автоматические зарабатываются путём выполнения определённых действий, заданных в правилах достижений (комментарии, лайки, найденные пасхалки и т. п.). Начнём! Пойдём от общего к частному. Первый шаг — продумать логику процесса. Чтобы корректно оценивать текущий прогресс достижения из общей статистики (с учётом того, что пользователь может отменить действие — убрать лайк, удалить комментарий и т. д.), я набросал такую формулу: [текущий прогресс достижения] =             [общий прогресс действий достижения (getCountEventsByUser)]             - [количество полученных достижений (таблица «Полученные достижения»)]             * [количество в правиле настроенного достижения COUNT (таблица «Достижения»)] Далее если [текущий прогресс достижения] больше или равен [количество в правиле настроенного достижения] то [количество достижений для вручения (обычно одно)] =             [текущий прогресс достижения]             / [количество в правиле настроенного достижения COUNT (таблица «Достижения»)] [количество достижений для вручения (обычно одно)] округляем до целого числа в меньшую сторону, и столько достижений вручаем пользователю. Минимальный набор таблиц для достижений Переходим от идеи к реализации. Для начала определимся с ключевыми таблицами. Пользователи ID NAME 1 Вася 2 Петя 3 Ефим Достижения (их визуальная настройка и правила работы) ID NAME ICON RULE COUNT DESCRIPTION 1 Участие в турнирах 1.svg manual   Принять участие в турнире или   чемпионате 2 Лайки новостей 2.svg like_news 30 Полайкать 30 новостей на портале 3 Комментирование новостей 3.svg comment_news 10 Прокомментировать 10 новостей на   портале 4 Пасхалка 4.svg easter_secret 3 Найти 3 пасхалки на портале RULE — код правила достижения COUNT — количество действий для получения текущего достижения Как это выглядит в админке: Полученные достижения (список достижений, который уже получили пользователи) ID ACHIEVEMENT USER DATE_TIME COMMENT 1 2 (лайки новостей) 1 (Вася) 18-12-2022 18:00   2 1 (участие в турнирах) 2 (Петя) 19-12-2022 10:30 За победу в   шахматном турнире 3 4 (пасхалка) 2 (Петя) 20-12-2022 10:10   4 2 (лайки новостей) 3 (Ефим) 20-12-2022 11:10   ACHIEVEMENT — достижение, ID из таблицы «Достижения» USER — пользователь, ID из таблицы «Пользователи» DATE_TIME — когда было получено достижение COMMENT — комментарий для достижений с типом «ручное» Можно было, конечно, сделать для каждого достижения счётчик прогресса и счётчик полученных достижений пользователя, но так как подсчёт общего прогресса оказался ничтожным по нагрузке, я решил не включать их в таблицу. Ручные правила назначаются, ясное дело, вручную администратором портала за какие-либо заслуги путём добавления записи в таблицу «Полученные достижения» с помощью CMS-интерфейса либо другим удобным способом (например, в том же разделе в CMS будет кнопка «Вручить достижение», где нужно будет указать вид достижения, вписать имя сотрудника и добавить комментарий). А вот с автоматическими правилами всё гораздо сложнее. Здесь необходимо было продумать подсчёт прогресса достижения при целевом действии пользователя и задать правила таким образом, чтобы впоследствии было несложно добавлять новые, не дописывая кучу кода в разные файлы. Реализация добавления новых правил через интерфейс CMS была бы слишком громоздкой, так как правила могут быть совершенно разными и предусмотреть их все заранее невозможно. Наша внутрикорпоративная система лояльности сейчас развивается очень бурно; на данный момент на портале реализовано большое число сущностей, которые пополняются практически еженедельно. Сделать всё это в одном файле в виде свойств пользователя, когда у ачивки просто перещелкивается флажок о полученном достижении, — тоже не вариант. Каждый пользователь может получить энное количество достижений каждого типа, и для статистики нужно вести время их получения в отдельной таблице. Вместо этого я решил сделать классы-правила. Каждое правило — это отдельный класс модуля с минимальным набором наследуемых абстрактных свойств и методов, которые необходимо реализовать для корректной работы правила. Структура модуля: Далее я разбираю каталог классов-правил из директории Rules, исключая абстрактные классы и интерфейсы, и вывожу в интерфейсе настроек достижений в виде html-селекта (отдельное кастомное свойство IblockPropertyRules.php), взяв названия из свойства класса $params[‘name’] и value из $params[‘code’]. Рассмотрим пример: Имеется специально разработанный нами модуль Achievements, в котором находятся различные вспомогательные классы для работы с ним. Achievements / lib / Achievements.php — основной класс модуля (получение списка достижений getList со списком правил с текущим прогрессом пользователя и метод проверки прогресса пользователя в момент целевого события-действия с отправкой выполненного достижения checkProgress). Метод checkProgress пробегается по всем правилам и проверяет, не выполнилось ли одно из них. Achievements / lib / Rules / — здесь лежат классы-правила. Вот один из них, отвечающий за комментарии в блоге новостей: Выглядит довольно просто и компактно. В каждом классе-правиле (по структуре они все одинаковые, кроме названия самого класса и параметров с выборкой) необходимо задать: params — параметры правила: название (name), код (code), автоматическое (auto) или нет (true/false), сортировка правил (sort) для вывода в интерфейсе CMS; getCountEventsByUser — метод для получения общего количества действий (в данном примере — оставленных комментариев в блоге) текущего или выбранного пользователя (таким образом мы вычисляем прогресс достижения, для которого установлено данное правило). Ещё один пример класса-правила: Данный класс правил подсчитывает количество лайков новостей указанного пользователя с момента запуска системы достижений на публику портала, где: get — параметры класса-правила; getCountEventsByUser — получение количества лайков. Для подсчёта прогресса и автоматического вручения достижения необходимо использовать метод checkProgress, который будет срабатывать при каждом целевом действии (лайк, комментарий и т. п.). Система будет пробегаться по настроенным достижениям и привязанным к ним классам-правилам rule_code (таблица «Достижения») с auto=true, а также по количеству уже полученных достижений пользователя (таблица «Полученные достижения»). Далее из класса-правила будет получен прогресс действий getCountEventsByUser. После сверки с настройкой достижения COUNT (таблица «Достижения») из него будет вычтено количество полученных достижений соответствующего типа. В итоге текущий прогресс должен получиться больше либо равен настройке достижения. Пример части метода checkProgress: Напоследок — пример «пасхалки» на фронтенде. В нашем случае пасхалки — это запрятанные кнопки в ленте новостей на портале, этакий бонус для самых внимательных :) Спасибо, что уделили время! Надеюсь, моя статья была вам полезна. Если кому-то интересны подробности реализации системы на CMS «1С-Битрикс», спрашивайте в комментариях. Если вы придумали альтернативное решение — пишите, очень интересно «сверить показания» :)