package services import ( "bytes" "context" "fmt" "image" "image/color" "image/draw" "image/png" "log" "slices" "time" "github.com/boombuler/barcode" "github.com/boombuler/barcode/code128" "github.com/boombuler/barcode/ean" "github.com/boombuler/barcode/qr" "github.com/redis/go-redis/v9" ) type BarcodeService interface { GenerateBarcode(format string, content string, width int, height int, padding int) (bytes.Buffer, error) IsTypeSupported(format string) bool } type DefaultBarcodeService struct { RedisClient *redis.Client } func (b *DefaultBarcodeService) GenerateBarcode(format string, content string, width int, height int, padding int) (bytes.Buffer, error) { ctx := context.Background() if !b.IsTypeSupported(format) { return bytes.Buffer{}, fmt.Errorf("unsupported barcode type: %s", format) } if b.RedisClient != nil { cachedBarcode, err := b.RedisClient.Get(ctx, fmt.Sprintf("barcode:%s:%s:%d:%d:%d", format, content, width, height, padding)).Result() if err == nil { log.Printf("Cache hit for barcode:%s:%s:%d:%d", format, content, width, height) buf := bytes.Buffer{} buf.Write([]byte(cachedBarcode)) return buf, nil } } var generatedCode barcode.Barcode var err error switch format { case "ean13": generatedCode, err = ean.Encode(content) if err != nil { return bytes.Buffer{}, err } break case "code128": generatedCode, err = code128.Encode(content) if err != nil { return bytes.Buffer{}, err } break case "qr": generatedCode, err = qr.Encode(content, qr.M, qr.AlphaNumeric) if err != nil { return bytes.Buffer{}, err } break } // Create a white background image bg := image.NewRGBA(image.Rect(0, 0, width, height)) white := color.RGBA{255, 255, 255, 255} draw.Draw(bg, bg.Bounds(), &image.Uniform{white}, image.Point{}, draw.Src) // Calculate the new size for the barcode to fit within the padding newWidth := width - 2*padding newHeight := height - 2*padding // Scale the barcode to the new size scaledCode, err := barcode.Scale(generatedCode, newWidth, newHeight) if err != nil { return bytes.Buffer{}, err } // Draw the barcode on top of the white background with padding draw.Draw(bg, scaledCode.Bounds().Add(image.Point{padding, padding}), scaledCode, image.Point{}, draw.Over) var buf bytes.Buffer err = png.Encode(&buf, bg) if err != nil { return bytes.Buffer{}, err } if b.RedisClient != nil { err = b.RedisClient.Set(ctx, fmt.Sprintf("barcode:%s:%s:%d:%d", format, content, width, height), buf.String(), 10*time.Minute).Err() if err != nil { return bytes.Buffer{}, err } } return buf, nil } func (b *DefaultBarcodeService) IsTypeSupported(format string) bool { supportedTypes := []string{"ean13", "code128", "qr"} return slices.Contains(supportedTypes, format) }