Move inline code examples out of normative contracts
identifier-normalization, no-hardcoded-vendors, vendor-installer-verification, and build-version-display follow the go-database split: rules in contract.md, snippets in README.md. Routed contract reads get cheaper; examples stay available on demand. Lint now also rejects stale kit/patterns references. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
# Contract: Identifier Normalization
|
||||
|
||||
Version: 1.0
|
||||
Version: 1.1
|
||||
|
||||
## Purpose
|
||||
|
||||
Правила хранения и сравнения идентификаторов оборудования:
|
||||
серийные номера, вендоры, версии прошивок, партномера, артикулы.
|
||||
|
||||
---
|
||||
See `README.md` for Go and SQL examples.
|
||||
|
||||
## Правило
|
||||
|
||||
@@ -17,11 +17,8 @@ Version: 1.0
|
||||
```
|
||||
Пришло: "SN-001-ABC" → хранится: "SN-001-ABC"
|
||||
Пришло: "sn-001-abc" → это тот же объект, не дубликат
|
||||
Пришло: "Sn-001-Abc" → то же самое
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Применяется к полям
|
||||
|
||||
- Серийный номер (`serial_number`, `serial`)
|
||||
@@ -30,61 +27,13 @@ Version: 1.0
|
||||
- Партномер (`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));
|
||||
```
|
||||
|
||||
---
|
||||
- Go: сравнение только через `strings.EqualFold`, никогда `==`.
|
||||
- Go: ключ дедупликации в map — `strings.ToLower(value)`; сам объект хранит оригинал.
|
||||
- SQL-поиск: `WHERE LOWER(col) = LOWER(?)`.
|
||||
- Уникальность: MySQL/MariaDB — case-insensitive collation (`utf8mb4_unicode_ci`) +
|
||||
unique index; SQLite — `CREATE UNIQUE INDEX ... ON t (LOWER(col))`.
|
||||
|
||||
## Что не делать
|
||||
|
||||
|
||||
Reference in New Issue
Block a user