Руководство по Ревью Кода Scala/SQL (GlobalFramework)#
Это руководство служит рекомендацией для начинающих ревьюверов, указывая, на что необходимо обращать внимание при выполнении ревью кода. Оно содержит рекомендации, собранные разработчиками команды, и направлено на повышение качества, производительности и стабильности кода. Документ охватывает общие принципы, архитектурные и стилистические нормы, а также важные аспекты производительности и безопасности.
1. Метаописания и Структура Классов#
Именование классов#
Имя класса должно быть уникальным, на латинице в формате <Модуль>_<Имя> (единственное число, именительный падеж, например, Lbr_Book). Имена сущностей регистрозависимы и должны точно совпадать с ожиданиями фреймворка (CamelCase).
Цель: Обеспечить ясность структуры, избежать конфликтов, гарантируя корректную работу фреймворка и улучшить читаемость кода.
Именование атрибутов#
Атрибуты должны иметь осознанные имена, без транслита, соответствующие содержанию поля (camelCase).
Цель: Улучшить читаемость и документировать структуру данных, соответствовать стилю Scala и GSF.
Типы атрибутов#
Для каждого поля должно быть указано имя, тип данных и связь. Простые поля используют типы NLong, NNumber, NString, NDate и т.д.; ссылочные поля – Reference, VariableReference или GID. Проверьте, что все Reference указывают на существующий класс и тип правильный (например, GID для переменной ссылочности). Типы полей должны быть корректно указаны и соответствовать БД (например, NLong, NNumber, NBigDecimal, NString, NGid, NDate и т.п.).
Цель: Обеспечить корректную работу с null-значениями, безопасность типов, интеграцию с фреймворком и согласованность схемы БД и метаданных.
Соответствие БД и метаданных#
Названия и типы полей в метаданных должны совпадать с колонками в БД (NLong → bigint, NString → varchar/text, NDate → date). При джоинах убедитесь в совместимости типов (избегайте неявных преобразований идентификаторов и используйте корректные приведения).
Цель: Обеспечить корректную работу ORM, избежать ошибок типов и обеспечить производительность запросов.
Связи#
Проверить корректность внешних ключей/ссылок, отраженных в метаданных. Использовать GID для переменной ссылочности в одном поле. Убедиться, что все связи отражены в метаданных (поля помечены как Reference или VariableReference).
Цель: Обеспечить целостность данных, корректную навигацию по объектной модели и работоспособность связанных функций GSF.
JSON/Jsonb-поля#
Если используется динамическое расширение (JSONB), проверьте настройку контейнера (тип Json) и работающую схему ключ–значение. Также проверьте необходимость индексов.
Цель: Обеспечить эффективное хранение и извлечение гибких структур данных, при необходимости — повысить производительность поиска.
Отсутствие дубликатов#
Убедиться, что в метаописаниях нет дублирующих или неиспользуемых полей и неактуальных связей.
Цель: Улучшить читаемость метаданных, упростить поддержку, снизить вероятность логических ошибок и избыточной сложности модели.
2. Конфигурация Модулей GSF#
project.yaml#
Проверьте секции конфигурации проекта (версии Scala/Java, параметры сборки, список модулей). Например: scalaFeatureRelease, sbtPlugin, modules с описанием каждого модуля – имя, source (репозиторий), branch, флаг isPublish. Убедитесь, что указаны все нужные модули (например, calculation, source, target, custom при наличии) и их настройки верны.
Цель: Обеспечить корректную сборку проекта, управление зависимостями между модулями, использование нужных версий библиотек и плагинов.
Структура каталогов#
Поддерживайте корректную структуру каталогов (как сгенерировано Configurator), включая папки src, resources, тесты и пр.
Цель: Облегчить навигацию по коду, упростить настройку IDE и сборочных скриптов, повысить согласованность между проектами.
Зависимости#
Зависимости между модулями должны быть описаны явно в build.sbt, а так же продублированы в module-info.xml как метаданные
Цель: Обеспечить корректную сборку, изоляцию модулей, избежать проблем при сборке и развертывании.
Конфигурации сборки/публикации#
Конфигурации сборки (например, sbt) и плагины должны быть корректны. Названия модулей и подпроектов должны соответствовать соглашениям.
Цель: Обеспечить стабильность и воспроизводимость процесса сборки и публикации, улучшить читаемость и поддержку проекта.
3. Использование стандартных компонентов GSF#
Использование сервисов GSF#
По возможности используйте готовые сервисы фреймворка: аудит изменений, автонумерацию, группировку, поиск по шаблону, прикреплённые файлы и т.д. Например, если требуется фильтрация классов, включите «Универсальный фильтр» – он позволит фильтровать по полям класса и его коллекций на уровне БД. Группировка объектов должна работать через механизмы группировки GSF.
Цель: Снизить вероятность ошибок, ускорить разработку, обеспечить единообразие функциональности, упростить поддержку и обновление.
Кэширование и запросы#
Если используется кэширование GSF (Shared-cache), убедитесь, что для часто запрашиваемых сущностей настроены cache-index в ORM и при запросах используется .unique(). Используйте объектные запросы (OQuery) с tryCacheQueryResults() там, где это целесообразно. Проверьте стратегии инвалидирования кэша.
Цель: Повысить производительность за счёт снижения обращений к БД, обеспечить актуальность данных в кэше.
Логирование#
Для записи логов в базу задействуйте встроенный LogTransaction (выделяет отдельную лог-сессию). При большом количестве записей выполняйте commitByInterval() (например, каждые 1000 записей). Такой подход изолирует логи от основной транзакции и снижает нагрузку на БД.
Цель: Изолировать логи от основной транзакции, избежать потери логов при откате, снизить вероятность конфликтов и нагрузку на БД.
4. Обработка Ошибок и Зависимости#
Прикладные исключения#
В коде обрабатывайте только «прикладные» исключения — наследники AppException. Системные исключения не перехватывайте, а выбрасывайте дальше, пропускайте (так фреймворк сможет корректно прервать сессию).
Цель: Чётко разделить бизнес-логику ошибок и системные сбои, предотвратить скрытие серьёзных проблем и нарушение целостности данных.
Объявление исключений#
Создавайте свои исключения как подклассы AppException и заводите для них фабрику (например, object ExceptionName extends ExceptionFactory(new ExceptionName(_))). Выбрасывайте исключения через throw AppException(…) или e.raise(…) (сохранит стек вызовов). При необходимости логирования ошибки используйте отдельную лог-транзакцию.
Цель: Обеспечить единообразие, упростить обработку, легко идентифицировать тип ошибки, гарантировать запись лога при откате основной транзакции.
Логирование ошибок#
При необходимости логирования ошибки используйте отдельную лог-транзакцию. В try/catch используйте минимальное информативное логирование; причина исключения не скрывается.
Цель: Изолировать запись лога от основного процесса, избежать засорения логов, сохранить ключевую информацию для диагностики, сохранить причину исключения.
Транзакции и зависимые задачи#
Избегайте слишком длинных транзакций (несколько минут и более) – PostgreSQL плохо их переносит. Разбивайте большие операции на логические блоки и выполняйте session.commitWorkAuto() после каждой партии изменений. Например, при массовой загрузке в цикле вызывайте commitWorkAuto() через заданный интервал, чтобы регулярно сбрасывать пакет в БД. При этом используйте flush() / commit() осмотрительно: не делайте неожиданных коммитов внутри server-side методов, чтобы не прерывать ожидания отката.
Цель: Снизить вероятность конфликтов, рост WAL, улучшить общую производительность БД, уменьшить вероятность сбоев, облегчить откат, сохранить согласованность данных.
5. Особенности SQL в GSF (Валидации и Ограничения)#
Оптимизация запросов#
Всегда проверяйте план выполнения (EXPLAIN) для поиска «узких мест». Сложные запросы по возможности разбивайте на подзапросы, упрощая логику.
Цель: Выявить проблемы с производительностью, улучшить читаемость, потенциально повысить производительность.
Индексы и параметры#
При фильтрации используйте индексируемые поля (например, не фильтруйтесь по служебным столбцам типа gid без индекса). Всегда передавайте значения в запрос через параметры (? или именованные), чтобы избежать SQL-инъекций и позволить СУБД кэшировать планы.
Цель: Обеспечить быстрое выполнение запросов, защитить от SQL-инъекций, ускорить выполнение за счёт переиспользования планов.
Соединения и типы#
При JOIN таблиц по ключам используйте одинаковые типы полей (например, связывайте bigint с NLong); избегайте неявных преобразований (например, конвертации varchar и text). При необходимости приводите типы явно.
Цель: Обеспечить эффективное использование индексов, избежать дорогостоящих преобразований данных, улучшить производительность.
Рекурсивные запросы#
Если пишете рекурсивные CTE, обязательно добавьте условие выхода (обычно это WHERE id NOT IN (…) с ограничением глубины) и защиту от зацикливания. И используйте NOT EXISTS вместо NOT IN, чтобы учесть NULL и не терять результаты.
Цель: Предотвратить бесконечные циклы в запросе, обеспечить надёжность при наличии NULL-значений.
6. Требования к Структуре Scala-Кода#
Соглашения по стилю#
Соблюдайте стандарты именования Scala (классы – CamelCase, методы/переменные – camelCase) по руководству Scala. Global3-FrameWork регистрозависим, поэтому имена сущностей (классов, полей, методов) должны точно совпадать с тем, как они объявлены. Не используйте подчёркивания в именах (за исключением разделителя модуля и имени класса).
Цель: Обеспечить корректную работу фреймворка, повысить читаемость кода, облегчить его понимание и поддержку.
D/A-паттерн кода#
При генерации классов учтите, что фреймворк генерирует две иерархии: Domain- и Application-части. Доменные классы (ClassDpi, ClassDvi) перезаписываются при генерации и содержат «скелет» логики, а прикладные классы (ClassApi, ClassAvi) – расширяют их и используются для ручной бизнес-логики. Логика должна писаться только в прикладных классах (Api/Avi), доменные файлы не редактируйте.
Цель: Обеспечить стабильность при регенерации кода, изолировать бизнес-логику от автоматически генерируемого кода.
Null-типы GSF#
Для работы со значениями из БД используйте расширенные типы GSF (NLong, NNumber, NGid, NDate, NString, NBigDecimal и т.д.), которые безопасно обрабатывают null. Например, идентификаторы – NLong (фабрика nl), числовые поля – NNumber или NBigDecimal (фабрика nr). Важное правило: для финансовых расчётов используйте NBigDecimal (BigDecimal), чтобы избежать ошибок двоичной арифметики. При сравнении nullable-значений в скриптах GSF применяйте оператор === (предотвращает NPE) вместо стандартного ==.
Цель: Защитить от NullPointerException, обеспечить корректную обработку null, гарантировать точность вычислений для финансовых данных, обеспечить безопасность при сравнениях.
7. Общие Принципы и Стиль Кода#
Комментарии#
Код должен сопровождаться понятными комментариями.
Цель: Помочь другим разработчикам понять назначение и логику кода, поддерживать профессиональный стиль документации.
Именование#
Переменным и атрибутам необходимо давать осознанные имена, транслит необходимо избегать. Следуйте scala-конвенциям наименования сущностей.
Цель: Улучшить читаемость, документировать структуру данных, соответствовать стилю Scala и GSF.
Форматирование#
sql и scala код должны быть отформатированы и не превращаться в «кашу».
Цель: Улучшить читаемость, облегчить понимание и сопровождение кода.
Scaladoc#
Писать scaladoc к методам, классам и переменным.
Цель: Предоставить официальную документацию к API, облегчить поддержку и использование кода другими разработчиками.
8. Безопасность и Надёжность#
null#
Использование null должно быть полностью исключено для всех ссылочных типов.
Цель: Предотвратить NullPointerException, использовать безопасные GSF-типы.
null для AnyVal#
Для значимых типов (AnyVal), таких как JObject, запрещено использовать null или его приведение (null.asInstanceOf[JObject]). Вместо этого необходимо использовать пустые экземпляры (например, JObject()), а проверку на наличие данных выполнять через .nonEmpty.
Цель: Предотвратить NullPointerException при работе с такими типами.
head / get#
Необходимо избегать использования head или get; вместо них следует использовать headOption или getOption, чтобы избежать NPE.
Цель: Обеспечить безопасную обработку отсутствия значения, сделать код более надёжным.
if-else / pattern-matching#
При ветвлении кода if-else необходимо указывать ветку else; для pattern-matching обязателен случай case _ =>.
Цель: Гарантировать обработку всех возможных сценариев, предотвратить MatchError, сделать код более надёжным.
9. Производительность#
.view#
При использовании нескольких комбинаторов подряд (например, arr.map.flatMap.filter) необходимо добавлять .view в начало, чтобы не создавались временные коллекции.
Цель: Улучшить производительность за счёт снижения расхода памяти.
Pattern matching в комбинаторах#
При использовании комбинаторов по карте необходимо применять pattern matching для деструктуризации кортежей. Плохо: any_map.foreach { tpe => tpe._1 + tpe._2 }. Хорошо: any_map.foreach { case (key, value) => key + value }.
Цель: Сделать код более выразительным, типобезопасным и читаемым.
Изменяемые коллекции#
В параметрах методов и возвращаемых значениях не должны использоваться изменяемые коллекции collection.mutable.
Цель: Повысить безопасность и предсказуемость кода, уменьшить вероятность ошибок.
ArrayBuffer#
Необходимо использовать ArrayBuffer вместо ListBuffer.
Цель: Обеспечить лучшую производительность для операций вставки и доступа по индексу.
Выбор коллекции#
При выборе коллекции (List, Array, Vector, ArrayBuffer) необходимо учитывать асимптотическую сложность операций (вставка, доступ по индексу), чтобы обеспечить оптимальную производительность.
Цель: Использовать подходящие структуры данных для ускорения выполнения операций.
Большие реестры#
Получение данных из больших реестров должно выполняться реляционными запросами в БД по максимальному количеству условий с предварительным session.flush(), чтобы не перегружать ОЗУ сервера.
Цель: Снизить нагрузку на память сервера приложений, повысить производительность за счёт фильтрации на уровне БД.
10. Работа с API и ROP#
HTTP-пакеты#
Прямое обращение к http пакетам запрещено; необходимо использовать btk_httppkg.
Цель: Обеспечить централизованное управление HTTP-вызовами, соблюдение стандартов безопасности и логирования.
Файлы#
При работе с файлами необходимо использовать стандартное api — btk_fileapi.
Цель: Гарантировать корректную обработку файлов, безопасность и совместимость с инфраструктурой.
copyAro#
Метод copyAro не должен использоваться, так как все данные провайдера строки копируются в оперативную память.
Цель: Предотвратить чрезмерный расход памяти, особенно при работе с большими объектами или в циклах.
Загрузка ROP по gid#
При загрузке ропов по gid необходимо использовать Btk_Pkg.loadByGid (обёрнутый в TryApp). Полученный rop должен матчиться с нужным API, например: case Btk_ClassApi(rop) =>.
Цель: Обеспечить безопасную загрузку объектов и правильную их типизацию.
Поиск по коду#
Для поиска по коду необходимо использовать методы ____Api().findByMnemoCode(""), а не прямые запросы. Объекты API должны быть объявлены через lazy val.
Цель: Использовать встроенные механизмы GSF, повысить читаемость и поддерживаемость кода.
getVar#
Вместо конструкции api().load(getVar("idAnyClass").asNLong) необходимо использовать метод get, так как нет гарантии, что getVar вернёт значение вместо null.
Цель: Обеспечить более безопасное и надёжное получение данных.
Использование source-generated конструкций#
Для доступа к атрибутам необходимо использовать конструкции, сгенерированные генератором источников (например, rop.get(_.attr)), а не методы getByAttrName и getAttrByName, так как последние используют рефлексию, ошибки которой могут быть обнаружены только во время выполнения.
Цель: Обеспечить безопасность на этапе компиляции, предотвратить ошибки, связанные с рефлексией.
11. Работа с Базой Данных и SQL (Дополнения)#
checkworkability#
Обращение к базе данных в checkworkability должно быть исключено; нужные данные необходимо выносить в onRefresh и делать доступными через getVar.
Цель: Критично для производительности UI, предотвратить неотзывчивость интерфейса.
Вложенные циклы#
Запросы внутри вложенных циклов использовать нельзя.
Цель: Избежать экспоненциального роста числа запросов к БД, сохранить производительность.
onRefresh#
В onRefresh необходимо использовать refreshByKey(parent) вместо byKey(parent).
Цель: Обновить данные в кэше, повысить эффективность.
@FlushBefore#
При необходимости использования onRefresh на основе selectStatement в детализации документа необходимо отключать @FlushBefore, чтобы избежать ошибок при создании документа с обязательными атрибутами.
Цель: Предотвратить конфликты сессии и ошибки при инициализации документа.
OQuery и итерация#
При прогрузке в кеш через OQuery необходимо использовать итерацию по списку или получение элемента в конце, что гарантирует выполнение запроса.
Цель: Обеспечить корректное поведение ленивых запросов.
OQuery и условия#
Условия в OQuery, зависящие от полей, которые могут измениться в кеше, должны обрабатываться одним из способов: предварительный session.flush(); вынос условия в .filter результата; использование транзакционного индекса.
Цель: Гарантировать, что условия в запросе будут учитывать актуальное состояние данных.
Интерполяция строк#
Вместо интерполяции строк необходимо использовать метод on в sql запросах.
Цель: Обеспечить безопасную параметризацию, защитить от SQL-инъекций.
Symbol в on#
При передаче параметров в запросы на основе Anorm через метод on необходимо использовать Symbol("idGds"), а не символьные литералы через апостроф ('idGds), так как последний устарел.
Цель: Соответствовать современным стандартам, обеспечить корректную работу.
12. Архитектура и Дизайн#
lazy#
При объявлении переменных в классе необходимо использовать ключевое слово lazy, чтобы они инициализировались только при первом использовании.
Цель: Улучшить производительность, избежать ненужных вычислений.
dataInstall#
При регистрации типов, закладок, атрибутов, функциональных настроек, процедур в dataInstall необходимо добавлять lazy val с мнемокодами и id для зарегистрированных значений.
Цель: Улучшить читаемость и поддержку кода установки данных.
Изменение параметров#
При редактировании метода типы параметров не должны изменяться, это сохраняет обратную совместимость.
Цель: Критично для стабильности API, предотвратить ошибки в использующем коде.
Новые параметры#
Перед добавлением новых параметров в метод необходимо учитывать обратную совместимость. Новые необязательные параметры необходимо добавлять в конец сигнатуры метода.
Цель: Избежать необходимости изменять весь код, который вызывает этот метод.
Сложная логика в AVI#
Сложную логику не нужно расписывать в операции Avi, если она может быть вынесена в API/PKG. AVI-операция должна сводиться к вызову метода из API/PKG, это упрощает поддержку и минимизирует дублирование кода.
Цель: Улучшить поддерживаемость кода, разделить ответственности, тестировать бизнес-логику отдельно от UI-логики.
Scala-классы с компаньоном#
При создании структур Scala-классов с объектом-компаньоном необходимо использовать решение, позволяющее проектное переопределение: def list(): List = { new List {...} }.
Цель: Обеспечить более гибкую настройку поведения классов на уровне проекта.
13. Работа с Атрибутами и Полями (Дополнения)#
Source-generated доступ#
Для доступа к атрибутам необходимо использовать конструкции, сгенерированные генератором источников (например, rop.get(_.attr)), а не методы getByAttrName и getAttrByName, так как последние используют рефлексию, ошибки которой могут быть обнаружены только во время выполнения.
Цель: Обеспечить безопасность на этапе компиляции, предотвратить ошибки, связанные с рефлексией.
getSelfVar с A.idPerson.name#
В выборках при обращении к стандартным атрибутам необходимо использовать getSelfVar(A.idPerson.name) или просто A.idPerson.asNLong вместо getSelfVar("idPerson"), это позволяет поймать ошибки во время компиляции при удалении атрибута.
Цель: Обеспечить безопасность типов на этапе компиляции.
Хранимые поля в AVI#
Хранимые поля класса в AVI должны получаться через объект А, сгенерированный генератором источников, чтобы избежать ошибок времени выполнения при переименовании атрибута в классе.
Цель: Сделать код устойчивым к переименованиям атрибутов.
var в case class в onRefresh#
В case class’ах, которые возвращаются в onRefresh, атрибуты должны быть мутабельными (var).
Цель: Обеспечить корректную работу с UI и обновление данных.
14. Строки и JSON#
Интерполяция строк#
При сборке строк из статических и динамических элементов необходимо использовать интерполяцию s"text${value}otherText" или многострочный вариант: s"""1-st line |2-nd line |.... |last line""".stripMargin. Интерполяция должна использоваться вместо конкатенации, так как она оптимальнее и более читаема.
Цель: Повысить читаемость, обеспечить оптимизацию компилятором.
.stripMargin#
Для многострочных строк необходимо использовать | и .stripMargin, чтобы сохранить форматирование и структуру файла.
Цель: Улучшить читаемость многострочных строк в коде.
StringBuilder#
При сборке очень больших строк необходимо использовать StringBuilder вместо pos.map(s"...").mkString().
Цель: Обеспечить более эффективную работу с большими строками.
scala.util.parsing.json#
Использовать нельзя, предпочтительнее JObject.
Цель: Использовать интегрированный с GSF и более безопасный тип.
Btk_JsonPkg#
Использование Btk_JsonPkg необходимо избегать из-за зафиксированного разного поведения при его использовании с JObject.
Цель: Предотвратить неожиданное поведение и ошибки.
15. Специфика UI: AVI и Списочные Отображения#
getVar / getSelfVar / A в checkWorkAbility#
В Avi.checkWorkAbility и других AVI-операциях списочных отображений значения полей необходимо получать через getVar, getSelfVar или A, а не через thisRop(), чтобы избежать ошибки load id = null в пустом списке.
Цель: Предотвратить ошибки при работе с пустыми списками.
Поля с большим объёмом данных#
В списочные отображения не должны выводиться поля с большим объёмом данных, чтобы не перегружать сервер. Например, поле sResponse журнала сообщений должно быть вынесено на закладку детализации или храниться в файле.
Цель: Повысить производительность UI и улучшить пользовательский опыт.
@FlushBefore в LookUp#
В LookUp-отображениях для onRefresh необходимо добавлять аннотацию @FlushBefore(mode = FlushBeforeMode.Disabled).
Цель: Предотвратить нежелательные сбросы сессии при обновлении данных в LookUp.
DefaultRep#deactivateOper#
Вместо oper.isActive = someCondition необходимо использовать DefaultRep#deactivateOper для деактивации операций при невыполнении условий, это обеспечивает корректную работу при различных перекрытиях.
Цель: Использовать встроенный механизм фреймворка для корректной деактивации операций.
prepareSelectStatement#
Сортировка по умолчанию и передача макросов фильтрации должны быть указаны в prepareSelectStatement, а не в selectStatement.
Цель: Улучшить структуру и читаемость кода настройки выборки.
16. Специфика UI: Кэширование, Логика и Настройки#
Переходы состояний#
Переходы состояний должны определяться сравнением nOrder и формированием условия из двух частей (из какого в какое), например: nvStateFrom < 100.nn && nvStateTo >= 100.nn, это обеспечивает работоспособность логики даже при удалении перехода.
Цель: Сделать логику переходов устойчивой к изменениям структуры состояний.
@Setter(refreshAfter = true)#
Аннотация @Setter(refreshAfter = true) должна выполнять selection.refreshItem() в конце сеттера.
Цель: Обеспечить обновление UI после изменения значения.
Кастомные выборки#
Редактируемые кастомные выборки должны быть реализованы объектным запросом, чтобы собрать пул изменений и отправить их в БД одной транзакцией.
Цель: Повысить эффективность, обеспечить целостность данных.
onRefreshExt и id#
При реализации кастомных выборок на объектном запросе результат выборки должен содержать поле id для работы onRefreshExt.
Цель: Обеспечить корректную работу механизма обновления расширенных данных.
AdditionalInfo#
Использование AdditionalInfo необходимо дважды обдумывать. Предпочтительнее получать данные через onRefreshExt.
Цель: Улучшить структуру и производительность, избегать избыточного кода.
AdditionalInfo2#
При переопределении отображения, в котором уже используется AdditionalInfo, новые вычисляемые атрибуты не должны добавляться в новый AdditionalInfo2. В OnRefresh необходимо использовать несколько case-классов: thisApi().byParent(getIdMaster).map { rop => (rop, getAdditionalInfo(rop), getAdditionalInfo2(rop)) }.
Цель: Улучшить структуру и читаемость кода обновления данных.
17. Отражение (Reflection)#
Обоснование#
Использование reflection должно быть оправдано; в общем случае оно запрещено.
Цель: Повысить безопасность типов, производительность, упростить сопровождение кода.
scala.reflect.io._#
Использовать нельзя, предпочтительнее Java-классы.
Цель: Обеспечить лучшую стабильность и совместимость.
18. Специфика Модулей и Проектов#
Модуль STK#
Для получения остатков необходимо использовать ru.bitec.app.stk.Stk_Pkg#getRemainsMulti. Для получения цены необходимо использовать ru.bitec.app.stk.Stk_Pkg#getnPrCostConsSum.
Цель: Использовать стандартные, оптимизированные методы для получения данных из модуля STK.
Модуль ACT#
Запрос оборотов должен выполняться с учётом логовой таблицы изменений через union all, чтобы получить полную и актуальную информацию.
Цель: Гарантировать корректность и полноту данных об оборотах.
Проектные модули#
При открытии MR необходимо указывать соответствующую ветку с учётом проекта, на котором разрабатывается. Например, для проекта СНГ разработка ведётся на sng-internal-dev → ветка проектного модуля gs; pdev → dev.
Цель: Обеспечить правильную интеграцию изменений в проектную ветку.
19. Безопасность и Целостность Данных#
SQL-инъекции#
Все входные данные параметризованы, нет риска SQL-инъекций.
Цель: Защитить от SQL-инъекций, ускорить выполнение за счёт переиспользования планов.
Ограничения целостности#
Ограничения целостности (foreign key, unique) соответствуют метаданным.
Цель: Обеспечить корректность и согласованность данных на уровне БД.
Валидации#
Валидации на уровне приложения дублируют, но не заменяют БД-ограничения.
Цель: Обеспечить дополнительный уровень защиты и пользовательскую обратную связь.
Чувствительные данные#
Чувствительные данные маскируются в логах и не попадают в открытые сообщения об ошибках.
Цель: Защитить конфиденциальную информацию от утечки.
20. Соответствие Best Practices#
Документация#
Убедитесь, что код и конфигурации соответствуют рекомендациям из официального руководства (например, настройка логирования, работа с транзакциями, параллельные вычисления и т.д.). Всегда проверяйте разделы «Практика разработки» в документации GlobalFramework и сверяйтесь с примерами.
Цель: Повысить качество, поддерживаемость и безопасность кода, облегчить его понимание новыми разработчиками.
Чистота кода#
Код должен быть читабельным и понятным (часто читают больше, чем пишут). Держите методы короткими, избегайте дублирования. Имена должны ясно отражать назначение сущности. Следуйте общим принципам GSF: не используйте служебные символы (например, «!», «?», «@») в идентификаторах, оформляйте документацию и комментарии по необходимости.
Цель: Улучшить читаемость, облегчить понимание и сопровождение кода.
21. Критические ошибки#
DML через ASQL#
DML (INSERT/UPDATE/DELETE) выполняется через ASQL. — Блокирующее
Цель: Предотвратить нарушение целостности данных из-за автокоммита ASQL.
Долгая транзакция#
Долгая транзакция, захватывающая большое число строк без разбивки. — Блокирующее
Цель: Избежать проблем с производительностью и блокировками БД.
Функции GSF в массовых выборках#
Массовое использование getattribute/getmnemocode/getheadline в больших выборках/отчётах. — Блокирующее
Цель: Избежать чрезмерной нагрузки на БД.
Арифметика с N-типами#
Арифметика с N-типами без проверки nullable (.isNotNull). — Блокирующее
Цель: Предотвратить NullPointerException и ошибки времени выполнения.
Конкатенация строк SQL#
SQL-строки формируются конкатенацией с пользовательскими данными. — Блокирующее
Цель: Предотвратить уязвимость к SQL-инъекциям.
Дублирование логики#
Необъяснимое и несогласованное дублирование логики в разных модулях. — Блокирующее
Цель: Упростить сопровождение, снизить вероятность ошибок, упростить внесение изменений.