Service REST
Introduction
Le groupeur GPPH peut être démarré comme service HTTP local pour grouper des cas via des requêtes JSON. Cela est utile pour appeler le groupeur depuis vos propres applications, sans redémarrer la CLI pour chaque cas.
Le service est destiné à un usage local et n'est pas conçu pour
être exposé sur Internet. Il s'attache par défaut à
127.0.0.1 et ne fournit pas de prise en charge TLS.
Si TLS est nécessaire, le service peut être placé derrière un
reverse proxy.
Les exemples suivants utilisent les outils en ligne de commande
curl et jq ; tout autre client HTTP
(p. ex. Postman) fonctionne également.
Démarrer le service
Le service se démarre depuis la
ligne de commande avec
l'option --service :
splg-grp-cli.exe --service
Ou bien, dans la variante Java :
java -jar app/splg-grouper-cli.jar --service
La définition et la liste hospitalière effectivement utilisées pour une requête sont déterminées par la version indiquée dans l'URL. Les fichiers correspondants sont chargés depuis le dossier de configuration livré, à la première requête, et conservés en mémoire pour la durée du service.
--service- Active le mode service.
--cfg-dir <path>- Optionnel : dossier de configuration alternatif. Par défaut, le dossier livré avec l'application est utilisé ; cette option n'est pas nécessaire en utilisation normale.
--host <hostname>- Adresse sur laquelle le serveur écoute. Par défaut :
127.0.0.1(accessible uniquement localement). Avec0.0.0.0, le service devient accessible sur toutes les interfaces réseau. --port <n>- Port TCP. Par défaut :
8080. --max-threads <n>- Nombre maximal de requêtes traitées simultanément. Par
défaut :
32. Les connexions excédentaires sont immédiatement rejetées avec503 Service Unavailable, plutôt que mises en file d'attente sans limite — la consommation mémoire reste ainsi bornée même sous charge. --read-timeout-ms <n>- Délai de lecture par connexion, en millisecondes. Par
défaut :
30000. Protège contre les clients lents (connexions de type Slowloris). --max-body-bytes <n>- Taille maximale du corps de requête, en octets. Par défaut :
4194304(4 Mio). Les requêtes plus volumineuses sont rejetées avec413 Payload Too Largesans allouer de mémoire. --graceful-stop-ms <n>- Délai d'extinction, en millisecondes. Par défaut :
10000. Sur Ctrl+C ouSIGTERM, le service cesse d'accepter de nouvelles connexions et attend jusqu'à ce délai la fin des requêtes en cours. --spitalliste <file>- Fichier de liste hospitalière facultatif (peut être indiqué plusieurs fois). S'il est indiqué, il est utilisé pour toutes les requêtes. Sans cette option, le service utilise la liste hospitalière standard de la version.
--sl <entrée>- Entrée de liste hospitalière en ligne (peut être indiquée plusieurs fois). Même syntaxe que sur la page Cas de test.
--wohnkanton-override <kanton>- Force le canton de domicile pour tous les cas.
Au démarrage, le service signale son socket d'écoute sur la sortie standard. Il s'arrête avec Ctrl+C.
Requêtes
L'entrée et la sortie des requêtes se font en JSON. La sortie
n'est pas formatée ; dans les exemples, elle est mise en forme
avec jq pour la lisibilité.
GET /version
Renvoie la version du service.
> curl -s http://127.0.0.1:8080/version | jq .
{
"status": 200,
"text": "OK",
"result": {
"version": "2026.2.1"
}
}
GET /healthz
Sonde de vivacité (liveness check) pour la supervision et les
reverse proxies. Répond toujours 200 OK sans
toucher à la configuration ni au système de fichiers — adapté
aux appels fréquents par systemd, nginx ou un système de
monitoring externe.
> curl -s http://127.0.0.1:8080/healthz | jq .
{
"status": "ok"
}
POST /group/<Version>
Groupe les données de cas transmises avec la définition GPPH indiquée et renvoie le résultat.
> curl -s http://127.0.0.1:8080/group/A_2026 \
-H "Content-Type: application/json" \
-X POST \
-d '{"behandlungen":[{"code":"815121"}]}' | jq .
{
"status": 200,
"text": "OK",
"result": {
"splg": "BEW7.1.1",
"quer": [],
"mfzs": ["BEW7.1.1"],
"mfzo": ["BEW7.1.1"],
"points": [],
"lactrl": 99,
"lactrlcodes": [],
"notes": "",
"errorcode": "w31",
"configuration": {
"defversion": "A_2026_3",
"spitallisten": ["A_2026_1_zh"]
}
}
}
Même avec un seul traitement défini, le cas est groupé avec
succès et la GPPH BEW7.1.1 est renvoyée.
Structure de la réponse
Chaque réponse du service est un objet JSON comportant trois champs :
status(nombre)- Code de statut numérique, miroir du code de statut HTTP
(p. ex.
200en cas de succès). text(chaîne)- Brève description textuelle du statut (p. ex.
"OK"ou un message d'erreur). result(objet, facultatif)- En cas de succès, le résultat proprement dit. Omis en cas d'erreur.
Lorsqu'un groupage a réussi, result contient les
champs suivants :
splg(chaîne)- Groupe de prestations de planification hospitalière déterminé pour le cas. Chaîne vide si aucune GPPH n'a pu être attribuée.
quer(tableau de chaînes)- Groupes de prestations transversales auxquels le cas est également affecté.
mfzs(tableau de chaînes)- Groupes MFZS (nombre minimal de cas par hôpital) auxquels le cas est affecté.
mfzo(tableau de chaînes)- Groupes MFZO (nombre minimal de cas par opérateur) auxquels le cas est affecté.
points(tableau d'objets)- Points des opérateurs. Chaque entrée contient
gln(GLN de l'opérateur),splg(GPPH associée) etpoints(points numériques). lactrl(nombre)- Statut du contrôle des prestations (p. ex.
0pour « mandat de prestations présent »,99pour « mandat inconnu ou cas non groupable »). Liste complète des codes : LACTRL. lactrlcodes(tableau de chaînes)- Codes ayant contribué à l'évaluation lactrl.
notes(chaîne)- Remarques additionnelles sur le groupage ; souvent vide.
errorcode(chaîne)- Statut de groupage (p. ex.
"0"pour un groupage sans erreur,"w31"pour l'avertissement « diagnostic principal manquant »). Liste complète des codes : ERROR. En présence de plusieurs codes, celui ayant la priorité la plus élevée est renvoyé. configuration(objet)- Documentation de la configuration effectivement utilisée (voir ci-dessous).
Le bloc configuration permet de retracer avec
quelle définition et quelle liste hospitalière un résultat
précis a été produit. C'est utile lorsque le service traite
plusieurs versions, ou lorsque les définitions ou listes
hospitalières varient entre exécutions du service :
defversion(chaîne)- Identifiant de version de la définition GPPH utilisée
(p. ex.
"A_2026_3"). Permet de rattacher sans ambiguïté un résultat à une définition donnée. spitallisten(tableau de chaînes)- Identifiants de version des listes hospitalières utilisées lors du groupage. Si plusieurs listes sont combinées, plusieurs entrées apparaissent.
Réponses d'erreur
400 Bad Request- Le corps est manquant ou n'est pas un JSON valide.
403 Forbidden- Définition disponible, mais licence manquante ou non valide pour cette version.
404 Not Found- Version non installée (aucun sous-dossier correspondant dans le dossier de configuration) ou URL inconnue.
413 Payload Too Large- Le corps de la requête dépasse la limite configurée par
--max-body-bytes. 500 Internal Server Error- Erreur inattendue lors du groupage. Le service continue de fonctionner.
503 Service Unavailable- Le service est saturé (tous les workers
--max-threadssont occupés). La requête peut être réessayée ultérieurement.
Données de cas (JSON)
Les données de cas à grouper sont transmises au service en
JSON. La structure correspond au
fichier JSON SPLG
existant (sans le conteneur splg-json) et s'appuie
sur le
format d'entrée
abstrait.
{
"burnr": "12345678",
"agey": "45",
"aged": "0",
"austritt": "20230227",
... autres champs selon le format abstrait ...
"diagnosen": [
{ "code": "D100", "zusatz": "", "seitigkeit": "", "rang": 0 },
{ "code": "D200", "seitigkeit": "", "rang": 1 }
...
],
"behandlungen": [
{
"code": "815121",
"seitigkeit": "",
"ambext": "",
"beginn": "2023022408",
"rang": 0,
"operateure": [
{ "gln": "7601234567890", "funktion": "1", "zulassung": "1" }
]
}
...
],
"bewegungen": [
{
"beginn": "20230224",
"ende": "20230227",
"art": "1",
"burnr": "12345678"
}
]
}
Tous les champs sont optionnels ; pour qu'un groupage
correct soit possible, les éléments pertinents (diagnostics,
traitements, âge, etc.) doivent toutefois être présents. À
l'exception des champs techniques rang, tous les
champs contiennent des chaînes ; les champs vides peuvent aussi
être complètement omis. À la place de bewegungen,
patientenbewegungen est également accepté pour
rester compatible avec le fichier SPLG-JSON existant.
Déploiement sous Linux (systemd + nginx)
Pour un fonctionnement durable, il est recommandé d'exécuter le service comme unité systemd et de placer un reverse proxy nginx devant lui. systemd gère le démarrage, le redémarrage en cas de crash et l'arrêt propre ; nginx s'occupe de TLS, du rate-limiting, du journal d'accès et, le cas échéant, de l'authentification — autant de tâches qui ne sont pas implémentées dans le service lui-même.
Unité systemd
Une unité minimale (à adapter aux chemins et à l'utilisateur) :
[Unit]
Description=SPLG-Grouper REST service
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=splg
Group=splg
WorkingDirectory=/opt/splg-grouper
ExecStart=/opt/splg-grouper/app/splg-grp-cli \
--service \
--host 127.0.0.1 \
--port 8080 \
--max-threads 32 \
--read-timeout-ms 30000 \
--max-body-bytes 4194304 \
--graceful-stop-ms 10000 \
--cfg-dir /opt/splg-grouper/cfg
KillSignal=SIGTERM
TimeoutStopSec=15
Restart=on-failure
RestartSec=5
RuntimeMaxSec=24h
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
[Install]
WantedBy=multi-user.target
Remarques :
--host 127.0.0.1garantit que le service n'est accessible que localement ; l'extérieur ne l'atteint qu'à travers nginx.TimeoutStopSecdoit être au moins égal à--graceful-stop-ms, afin que les requêtes en cours puissent se terminer proprement lors d'unsystemctl stop.RuntimeMaxSec=24hredémarre le service une fois par jour automatiquement — une assurance peu coûteuse contre une croissance lente de la mémoire en exécution prolongée.- L'unité s'installe dans
/etc/systemd/system/splg-grouper.service, puis s'active avecsystemctl daemon-reloadetsystemctl enable --now splg-grouper.
Reverse proxy nginx
Bloc serveur nginx simplifié pour la terminaison TLS et la redirection vers le service local :
limit_req_zone $binary_remote_addr zone=splg_grp_rl:10m rate=20r/s;
upstream splg_grouper_backend {
server 127.0.0.1:8080;
keepalive 8;
}
server {
listen 443 ssl http2;
server_name grouper.example.org;
ssl_certificate /etc/letsencrypt/live/grouper.example.org/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/grouper.example.org/privkey.pem;
client_max_body_size 4m;
proxy_connect_timeout 5s;
proxy_read_timeout 60s;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
location = /healthz {
proxy_pass http://splg_grouper_backend;
}
location / {
limit_req zone=splg_grp_rl burst=40 nodelay;
limit_req_status 429;
proxy_pass http://splg_grouper_backend;
}
access_log /var/log/nginx/splg-grouper-access.log combined;
}
Important :
client_max_body_sizedoit correspondre à--max-body-bytes; nginx rejette alors les requêtes trop volumineuses avant qu'elles n'atteignent le backend./healthzest transmis sans rate-limit, afin que la supervision puisse vérifier le service à tout moment.- Pour renforcer l'accès, on peut par exemple ajouter une
allowlist d'adresses IP (
allow/deny) ou exiger une clé d'API partagée dans un en-tête — utile, le service lui-même ne gérant aucune authentification.