refactor: устранить дублирование кода (Go-хендлеры, JS-автокомплит)

Найдено статическим анализом (dupl/jscpd) + проверено вручную:

Go:
- appstate/path.go: ResolveDBPath/ResolveConfigPath → общий resolvePath()
- handlers/respond.go: дженерик BindJSON[T] для bind+422-ошибки
- article/generator.go: buildNetSegment/buildPSUSegment → buildProfileSegment()
- cmd/qfs/main.go (+respond.go): 8 повторяющихся switch{case errors.Is(...)}
  → respondByErrCase(c, err, errCase{...}, ...)

Frontend (index.html):
- 5 идентичных обработчиков клавиатурной навигации автокомплита
  → handleAutocompleteKeyGeneric(event, onSelect)
- 4 функции сборки нового элемента корзины из автокомплита
  → buildNewCartItem()/commitCartChange()

Также: PricingMarkup — единая точка правды для аплифт-коэффициента
в JS (render + export), с cross-reference комментариями к Go-константам
в export.go (defaultSaleMarkup/stockCompetitorMarkupFactor).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Mikhail Chusavitin
2026-08-10 20:13:27 +03:00
co-authored by Claude Sonnet 5
parent b087b7eb58
commit 4d7b0e13ef
8 changed files with 199 additions and 314 deletions
+16 -31
View File
@@ -260,17 +260,28 @@ func buildDiskSegment(items []models.ConfigItem, cats map[string]string) (string
}
func buildNetSegment(items []models.ConfigItem, cats map[string]string) (string, string) {
return buildProfileSegment(items, cats, GroupNET, parsePortSpeed, "UNKNET", "net_unknown")
}
func buildPSUSegment(items []models.ConfigItem, cats map[string]string) (string, string) {
return buildProfileSegment(items, cats, GroupPSU, parseWatts, "UNKPSU", "psu_unknown")
}
// buildProfileSegment groups items of the given category group by a profile token
// parsed from their lot name (e.g. port speed, wattage rating), falling back to
// unknownToken and warnCode when a lot's profile can't be determined.
func buildProfileSegment(items []models.ConfigItem, cats map[string]string, group Group, parseProfile func(string) string, unknownToken, warnCode string) (string, string) {
groupQty := map[string]int{}
warn := ""
for _, it := range items {
group, ok := GroupForLotCategory(cats[it.LotName])
if !ok || group != GroupNET {
g, ok := GroupForLotCategory(cats[it.LotName])
if !ok || g != group {
continue
}
profile := parsePortSpeed(it.LotName)
profile := parseProfile(it.LotName)
if profile == "" {
profile = "UNKNET"
warn = "net_unknown"
profile = unknownToken
warn = warnCode
}
groupQty[profile] += it.Quantity
}
@@ -285,32 +296,6 @@ func buildNetSegment(items []models.ConfigItem, cats map[string]string) (string,
return strings.Join(parts, "+"), warn
}
func buildPSUSegment(items []models.ConfigItem, cats map[string]string) (string, string) {
groupQty := map[string]int{}
warn := ""
for _, it := range items {
group, ok := GroupForLotCategory(cats[it.LotName])
if !ok || group != GroupPSU {
continue
}
rating := parseWatts(it.LotName)
if rating == "" {
rating = "UNKPSU"
warn = "psu_unknown"
}
groupQty[rating] += it.Quantity
}
if len(groupQty) == 0 {
return "", ""
}
parts := make([]string, 0, len(groupQty))
for rating, qty := range groupQty {
parts = append(parts, fmt.Sprintf("%dx%s", qty, rating))
}
sort.Strings(parts)
return strings.Join(parts, "+"), warn
}
func normalizeModelToken(lotName string) string {
if idx := strings.Index(lotName, "_"); idx >= 0 && idx+1 < len(lotName) {
lotName = lotName[idx+1:]