diff --git a/internal/article/generator.go b/internal/article/generator.go index dc7dda0..ba8e9ef 100644 --- a/internal/article/generator.go +++ b/internal/article/generator.go @@ -141,7 +141,6 @@ func buildCPUSegment(items []models.ConfigItem, cats map[string]string) string { qty int } models := map[string]*agg{} - total := 0 for _, it := range items { group, ok := GroupForLotCategory(cats[it.LotName]) if !ok || group != GroupCPU { @@ -155,25 +154,16 @@ func buildCPUSegment(items []models.ConfigItem, cats map[string]string) string { models[model] = &agg{} } models[model].qty += it.Quantity - total += it.Quantity } - if total == 0 { + if len(models) == 0 { return "" } - if len(models) == 1 { - for model, a := range models { - return fmt.Sprintf("%dx%s", a.qty, model) - } + parts := make([]string, 0, len(models)) + for model, a := range models { + parts = append(parts, fmt.Sprintf("%dx%s", a.qty, model)) } - if len(models) <= 2 { - parts := make([]string, 0, len(models)) - for model, a := range models { - parts = append(parts, fmt.Sprintf("%dx%s", a.qty, model)) - } - sort.Strings(parts) - return strings.Join(parts, "+") - } - return fmt.Sprintf("%dxMIX", total) + sort.Strings(parts) + return strings.Join(parts, "+") } func buildMemSegment(items []models.ConfigItem, cats map[string]string) (string, string) { @@ -200,7 +190,6 @@ func buildMemSegment(items []models.ConfigItem, cats map[string]string) (string, func buildGPUSegment(items []models.ConfigItem, cats map[string]string) string { models := map[string]int{} - total := 0 for _, it := range items { group, ok := GroupForLotCategory(cats[it.LotName]) if !ok || group != GroupGPU { @@ -211,20 +200,16 @@ func buildGPUSegment(items []models.ConfigItem, cats map[string]string) string { model = "UNK" } models[model] += it.Quantity - total += it.Quantity } - if total == 0 { + if len(models) == 0 { return "" } - if len(models) <= 2 { - parts := make([]string, 0, len(models)) - for model, qty := range models { - parts = append(parts, fmt.Sprintf("%dx%s", qty, model)) - } - sort.Strings(parts) - return strings.Join(parts, "+") + parts := make([]string, 0, len(models)) + for model, qty := range models { + parts = append(parts, fmt.Sprintf("%dx%s", qty, model)) } - return fmt.Sprintf("%dxMIX", total) + sort.Strings(parts) + return strings.Join(parts, "+") } func buildDiskSegment(items []models.ConfigItem, cats map[string]string) (string, string) { @@ -233,7 +218,6 @@ func buildDiskSegment(items []models.ConfigItem, cats map[string]string) (string c string } groupQty := map[key]int{} - total := 0 warn := "" for _, it := range items { group, ok := GroupForLotCategory(cats[it.LotName]) @@ -247,9 +231,8 @@ func buildDiskSegment(items []models.ConfigItem, cats map[string]string) (string typeCode := diskTypeCode(cats[it.LotName], it.LotName) k := key{t: typeCode, c: capToken} groupQty[k] += it.Quantity - total += it.Quantity } - if total == 0 { + if len(groupQty) == 0 { return "", "" } parts := make([]string, 0, len(groupQty)) @@ -261,15 +244,11 @@ func buildDiskSegment(items []models.ConfigItem, cats map[string]string) (string } } sort.Strings(parts) - if len(parts) > 2 { - return fmt.Sprintf("%dxMIXD", total), warn - } return strings.Join(parts, "+"), warn } func buildNetSegment(items []models.ConfigItem, cats map[string]string) (string, string) { groupQty := map[string]int{} - total := 0 warn := "" for _, it := range items { group, ok := GroupForLotCategory(cats[it.LotName]) @@ -282,9 +261,8 @@ func buildNetSegment(items []models.ConfigItem, cats map[string]string) (string, warn = "net_unknown" } groupQty[profile] += it.Quantity - total += it.Quantity } - if total == 0 { + if len(groupQty) == 0 { return "", "" } parts := make([]string, 0, len(groupQty)) @@ -292,15 +270,11 @@ func buildNetSegment(items []models.ConfigItem, cats map[string]string) (string, parts = append(parts, fmt.Sprintf("%dx%s", qty, profile)) } sort.Strings(parts) - if len(parts) > 2 { - return fmt.Sprintf("%dxMIXN", total), warn - } return strings.Join(parts, "+"), warn } func buildPSUSegment(items []models.ConfigItem, cats map[string]string) (string, string) { groupQty := map[string]int{} - total := 0 warn := "" for _, it := range items { group, ok := GroupForLotCategory(cats[it.LotName]) @@ -313,9 +287,8 @@ func buildPSUSegment(items []models.ConfigItem, cats map[string]string) (string, warn = "psu_unknown" } groupQty[rating] += it.Quantity - total += it.Quantity } - if total == 0 { + if len(groupQty) == 0 { return "", "" } parts := make([]string, 0, len(groupQty)) @@ -323,9 +296,6 @@ func buildPSUSegment(items []models.ConfigItem, cats map[string]string) (string, parts = append(parts, fmt.Sprintf("%dx%s", qty, rating)) } sort.Strings(parts) - if len(parts) > 2 { - return fmt.Sprintf("%dxMIXP", total), warn - } return strings.Join(parts, "+"), warn } @@ -492,5 +462,141 @@ func compressArticle(segments []string) string { for _, s := range segments { normalized = append(normalized, strings.ReplaceAll(s, "GbE", "G")) } - return strings.Join(normalized, "-") + segments = normalized + article := strings.Join(segments, "-") + if len([]rune(article)) <= 80 { + return article + } + + // segment order: model, cpu, mem, gpu, disk, net, psu, support + index := func(i int) (int, bool) { + if i >= 0 && i < len(segments) { + return i, true + } + return -1, false + } + + // 1) remove PSU + if i, ok := index(6); ok { + segments = append(segments[:i], segments[i+1:]...) + article = strings.Join(segments, "-") + if len([]rune(article)) <= 80 { + return article + } + } + + // 2) compress NET/HBA/HCA + if i, ok := index(5); ok { + segments[i] = compressNetSegment(segments[i]) + article = strings.Join(segments, "-") + if len([]rune(article)) <= 80 { + return article + } + } + + // 3) compress DISK + if i, ok := index(4); ok { + segments[i] = compressDiskSegment(segments[i]) + article = strings.Join(segments, "-") + if len([]rune(article)) <= 80 { + return article + } + } + + // 4) compress GPU to vendor only (GPU_NV) + if i, ok := index(3); ok { + segments[i] = compressGPUSegment(segments[i]) + } + return strings.Join(segments, "-") +} + +func compressNetSegment(seg string) string { + if seg == "" { + return seg + } + parts := strings.Split(seg, "+") + out := make([]string, 0, len(parts)) + for _, p := range parts { + p = strings.TrimSpace(p) + if p == "" { + continue + } + qty := "1" + profile := p + if x := strings.SplitN(p, "x", 2); len(x) == 2 { + qty = x[0] + profile = x[1] + } + upper := strings.ToUpper(profile) + label := "NIC" + if strings.Contains(upper, "FC") { + label = "HBA" + } else if strings.Contains(upper, "HCA") || strings.Contains(upper, "IB") { + label = "HCA" + } + out = append(out, fmt.Sprintf("%sx%s", qty, label)) + } + if len(out) == 0 { + return seg + } + sort.Strings(out) + return strings.Join(out, "+") +} + +func compressDiskSegment(seg string) string { + if seg == "" { + return seg + } + parts := strings.Split(seg, "+") + out := make([]string, 0, len(parts)) + for _, p := range parts { + p = strings.TrimSpace(p) + if p == "" { + continue + } + qty := "1" + spec := p + if x := strings.SplitN(p, "x", 2); len(x) == 2 { + qty = x[0] + spec = x[1] + } + upper := strings.ToUpper(spec) + label := "DSK" + for _, t := range []string{"M2", "NV", "SAS", "SAT", "SSD", "HDD", "EDS", "HHH"} { + if strings.Contains(upper, t) { + label = t + break + } + } + out = append(out, fmt.Sprintf("%sx%s", qty, label)) + } + if len(out) == 0 { + return seg + } + sort.Strings(out) + return strings.Join(out, "+") +} + +func compressGPUSegment(seg string) string { + if seg == "" { + return seg + } + parts := strings.Split(seg, "+") + out := make([]string, 0, len(parts)) + for _, p := range parts { + p = strings.TrimSpace(p) + if p == "" { + continue + } + qty := "1" + if x := strings.SplitN(p, "x", 2); len(x) == 2 { + qty = x[0] + } + out = append(out, fmt.Sprintf("%sxGPU_NV", qty)) + } + if len(out) == 0 { + return seg + } + sort.Strings(out) + return strings.Join(out, "+") }