Service d'impression thermal ESC/POS avec système de queue distribué et persistence db et retry automatique.
Ce projet gère :
- Queue persistante : les jobs survivent aux redémarrages
- Retry automatique : backoff exponentiel sur les échecs
- Workers concurrents : traitement parallèle avec goroutines
- Recovery : récupération des jobs pending au démarrage
- Monitoring : API REST pour suivre l'état de chaque job
- Extensible : facile d'ajouter de nouveaux types de jobs et templates
- Supporte les codes-barres : CODE128, EAN13, EAN8, QR Code
- Go 1.21+ : concurrence native, channels, context
- SQLite : persistence des jobs (CGO requis)
- Gin : router HTTP léger
- ESC/POS : commandes raw pour imprimantes thermal
go mod download
go build -o print-service cmd/api/main.go┌─────────────────────────────────────────────────────┐
│ HTTP Handler │
│ (handlers/print.go, jobs.go, printer.go) │
└──────────────────────┬──────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ Queue Manager │
│ (queue/queue.go) │
│ - Enqueue jobs │
│ - Get status │
│ - List jobs │
└──────────────────────┬──────────────────────────────┘
↓
┌───────────┴───────────┐
↓ ↓
┌──────────────────┐ ┌──────────────────┐
│ Job Store │ │ Worker Pool │
│ (queue/job_store)│ │ (queue/worker.go)│
│ - SQLite │ │ - 5 workers │
│ - CRUD jobs │ │ - Consume queue │
└──────────────────┘ └────────┬─────────┘
↓
┌────────────────────────┐
│ Renderer │
│ (renderer/renderer) │
│ - ESC/POS generation │
└────────────┬───────────┘
↓
┌────────────────────────┐
│ Printer Service │
│ (printer/service.go) │
│ - Get printer config │
│ - TCP connection │
│ - Send to printer │
└────────┬───────────────┘
↓
┌────────────────┴────────────────┐
↓ ↓ ↓
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Registry │ │ Storage │ │ API Client │
│ (cache mem) │ │ (JSON file) │ │ (Laravel) │
│ - Fast get │ │ - Persist │ │ - Sync │
└──────────────┘ └──────────────┘ └──────────────┘
NOTE IMPORTANTE : go-sqlite3 nécessite CGO, il faut donc installer un compilateur C (jpense mingw-w64 est bon) et mettre CGO_ENABLED=1 Consultez aussi : le build dans les workflows GitHub Actions pour Windows qui montre comment faire.
pour définir la variable de façon permanente
go env -w CGO_ENABLED=1
pour la session powershell:
$env:CGO_ENABLED = "1
Pour lancer une seule commande avec CGO activé :
$env:CGO_ENABLED = "1"; go build -o .\cmd\api\api.exe .\cmd\api\main.go
On travaille avec le language ESC/POS qui travaille souvent en mono-octet (8 bits) via des codes pages et l'UTF-8 n'est pas tjr nativement compris par les modèles chinois ou autre qui ont souvent un double byte (dbcs) car ils ont firmware déstiné au marché asiatique avec des paires d'octets et souvent ça ignore complètement le text 8-bit "occidental" donc code page du self test n'est pas tjr la vérité absolue et ça c'est nul car le firmware CN peut rester en DBCS pour les raw data
l'idée est de tout simplement:
- désactiver le DBCS
FS .est la clé - passer en jeu international (France ajuste qlq symboles) : Touche seulement qlq glyphes mais ne remplace pas le code page
- activer le code page 1252 (Windows Estern) Je le mettrai pas dans le ENV, car c'est la dictature
- envoyer le texte brut en OCTETS EN CP12525
ESC @ (0x1B 0x40) // init
FS . (0x1C 0x2E) // DBCS OFF
ESC R 3 (0x1B 0x52 0x03)// jeu international
ESC t 16 (0x1B 0x74 0x10)// codepage = 1252 (Windows Western)
... octets en CP1252 ...
GS V 0 (0x1D 0x56 0x00)// cut
- init en go qu'on utilisera
var (
escInit = []byte{0x1B, 0x40}
fsExitCJK = []byte{0x1C, 0x2E}
escIntlFR = []byte{0x1B, 0x52, 0x03}
escCP1252 = []byte{0x1B, 0x74, 0x10}
cutFull = []byte{0x1D, 0x56, 0x00}
)ESC tchoisit le mapping 8-bit complet etESC Rajuste certains caractères spéciaux (symbole monétaire, diacritiques rares) selon la région- la plus part des printers ne gèrent pas l'UTF-8 brut en mode texte donc soit Transcoder en CP1252/850/858 avant envoi ou Rasteriser en image (bitmap) et imprimer l’image (plus lent, mais 100% fiable)
- Gestion des codes-barres via l'engine ESC/POS
- Supporte les formats:
- CODE128
- EAN13
- EAN8
- QR Code (si supporté par l'imprimante)
Pour le CODE128:
la hauteur est automatiquement ajustée
width peut être 2-6 (2 par défaut)
hri = human readable interpretation (texte lisible)
- Renderer → transforme data en ESC/POS
- Templates supportés:
- Base (template générique)
- Kitchen (commandes cuisine)
- Receipt (tickets de caisse)
- Customer (tickets clients)
- Data → Template choisi selon PrintType et caché dans le manager
- Template applique son layout
- Renderer génère les commandes ESC/POS ↓
- Output = raw bytes pour l'imprimante
Le point d'entrée pour créer des impressions multiples en une seule requête.
{
"order_number": "ORDER-12345",
"printings": {
"kitchen": "kitchen_printer",
"receipt": "receipt_printer",
"customer": "customer_printer"
},
"data": {
"items": ["..."],
"total": 45.50,
"payment_method": "card"
}
}- Validation : bind JSON → PrintRequest
- Conversion : req.ToRawData() → sérialise data en JSON brut
- Multi-job : pour chaque entry dans printings :
- Crée un PrintJob avec {type, printer, raw_data}
- Enqueue(job) → persist SQLite + envoi au channel
- Response 202 : retourne les IDs de chaque job créé
{
"message": "Jobs créés et envoyé aux workers",
"jobs": {
"kitchen": "abc-123-def",
"receipt": "ghi-456-jkl",
"customer": "mno-789-pqr"
}
}# Créer des impressions
POST /api/v1/print
{
"order_number": "12345",
"printings": {
"kitchen": "kitchen_printer",
"receipt": "receipt_printer"
},
"data": { ... }
}
# Lister les jobs
GET /api/v1/jobs?status=pending&limit=10
# Détails d'un job
GET /api/v1/jobs/:id
# Status rapide
GET /api/v1/jobs/:id/status
# Retry manuel (jobs dead)
POST /api/v1/jobs/:id/retry
# Statistiques
GET /api/v1/jobs/stats# Lister les imprimantes
GET /api/v1/printers
# Détails
GET /api/v1/printers/:name
# Status (online/offline)
GET /api/v1/printers/:name/status
# Test print
POST /api/v1/printers/:name/test
# Sync depuis API externe
POST /api/v1/printers/sync/api/print