From 91a1cc182d6ee373328bc670a1cac2bd5a5b0f86 Mon Sep 17 00:00:00 2001 From: Michael Chus Date: Sun, 1 Mar 2026 22:07:19 +0300 Subject: [PATCH] Add identifier normalization contract Co-Authored-By: Claude Sonnet 4.6 --- .../identifier-normalization/contract.md | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 rules/patterns/identifier-normalization/contract.md diff --git a/rules/patterns/identifier-normalization/contract.md b/rules/patterns/identifier-normalization/contract.md new file mode 100644 index 0000000..45ae460 --- /dev/null +++ b/rules/patterns/identifier-normalization/contract.md @@ -0,0 +1,93 @@ +# Contract: Identifier Normalization + +Version: 1.0 + +## Purpose + +Правила хранения и сравнения идентификаторов оборудования: +серийные номера, вендоры, версии прошивок, партномера, артикулы. + +--- + +## Правило + +Оригинальное значение **сохраняется как пришло** — регистр не меняется. +Все сравнения, поиск и дедупликация выполняются **без учёта регистра**. + +``` +Пришло: "SN-001-ABC" → хранится: "SN-001-ABC" +Пришло: "sn-001-abc" → это тот же объект, не дубликат +Пришло: "Sn-001-Abc" → то же самое +``` + +--- + +## Применяется к полям + +- Серийный номер (`serial_number`, `serial`) +- Вендор / производитель (`vendor`, `manufacturer`) +- Версия прошивки (`firmware_version`, `fw_version`) +- Партномер (`part_number`, `part_no`) +- Артикул (`article`, `sku`) + +--- + +## Реализация + +### Go — сравнение + +```go +import "strings" + +func SameIdentifier(a, b string) bool { + return strings.EqualFold(a, b) +} +``` + +### Go — дедупликация + +```go +func deduplicateBySerial(items []Device) []Device { + seen := make(map[string]struct{}) + result := items[:0] + for _, item := range items { + key := strings.ToLower(item.SerialNumber) + if _, exists := seen[key]; !exists { + seen[key] = struct{}{} + result = append(result, item) + } + } + return result +} +``` + +Ключ в map — всегда `strings.ToLower(value)`. Сам объект сохраняется с оригинальным значением. + +### SQL — поиск и уникальность + +Поиск: +```sql +SELECT * FROM devices WHERE LOWER(serial_number) = LOWER(?); +``` + +Уникальный индекс (MySQL / MariaDB): +```sql +-- Collation ci обеспечивает case-insensitive уникальность +ALTER TABLE devices MODIFY serial_number VARCHAR(255) + CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +ALTER TABLE devices ADD UNIQUE INDEX uniq_serial (serial_number); +``` + +SQLite: +```sql +CREATE UNIQUE INDEX uniq_serial ON devices (LOWER(serial_number)); +``` + +--- + +## Что не делать + +- Не приводить значение к нижнему или верхнему регистру перед сохранением. +- Не считать `"SN-001"` и `"sn-001"` разными объектами. +- Не использовать `==` для сравнения идентификаторов в Go — только `strings.EqualFold`.