REST-Service
Einführung
Der SPLG-Grouper kann als lokaler HTTP-Service gestartet werden und Fälle dann über JSON-Requests gruppieren. Das ist nützlich, um den Grouper aus eigenen Anwendungen heraus aufzurufen, ohne die CLI für jeden Fall neu zu starten.
Der Service ist für lokalen Gebrauch gedacht und nicht dafür,
über das Internet erreichbar zu sein. Er bindet sich
standardmässig an 127.0.0.1 und stellt keine
TLS-Unterstützung bereit. Wird TLS benötigt, kann der Service
hinter einen Reverse Proxy gestellt werden.
In den folgenden Beispielen werden die Kommandozeilen-Tools
curl und jq verwendet; jeder andere
HTTP-Client (z. B. Postman) funktioniert ebenfalls.
Service starten
Der Service wird über die Kommandozeile
mit der Option --service gestartet:
splg-grp-cli.exe --service
Bzw. in der Java-Variante:
java -jar app/splg-grouper-cli.jar --service
Welche Definition und ggf. Spitalliste für eine konkrete Anfrage verwendet wird, ergibt sich aus dem Release-Pfad in der URL. Die zugehörigen Dateien werden bei der ersten Anfrage aus dem mitgelieferten Konfigurationsordner geladen und für die Dauer des Service-Laufs im Speicher gehalten.
--service- Aktiviert den Service-Modus.
--cfg-dir <path>- Optional: alternativer Konfigurationsordner. Standardmässig wird der mitgelieferte Ordner verwendet; diese Option wird im Normalbetrieb nicht benötigt.
--host <hostname>- Adresse, auf der der Server lauscht. Standard:
127.0.0.1(nur lokal erreichbar). Mit0.0.0.0wird der Service auf allen Netzwerkschnittstellen erreichbar. --port <n>- TCP-Port. Standard:
8080. --max-threads <n>- Maximale Anzahl gleichzeitig bearbeiteter Anfragen.
Standard:
32. Übersteigende Verbindungen werden sofort mit503 Service Unavailablebeantwortet, statt unbegrenzt zu queuen — so bleibt der Speicherverbrauch auch unter Last beschränkt. --read-timeout-ms <n>- Lesetimeout pro Verbindung in Millisekunden. Standard:
30000. Schützt gegen langsame Clients (Slowloris-artige Verbindungen). --max-body-bytes <n>- Maximale Grösse des Request-Bodies in Bytes. Standard:
4194304(4 MiB). Grössere Anfragen werden mit413 Payload Too Largeabgelehnt, ohne den Speicher zu allozieren. --graceful-stop-ms <n>- Wartezeit beim Herunterfahren in Millisekunden. Standard:
10000. Bei Ctrl+C oderSIGTERMstoppt der Service die Annahme neuer Verbindungen und wartet bis zu dieser Zeit, bis laufende Anfragen abgeschlossen sind. --spitalliste <file>- Optionale Spitallistendatei (mehrfach angebbar). Wird für alle Requests verwendet, sofern angegeben. Ohne diese Option verwendet der Service die zum Release passende Standard-Spitalliste.
--sl <eintrag>- Inline-Spitallisteneintrag (mehrfach angebbar), gleiche Syntax wie auf der Testfall-Seite beschrieben.
--wohnkanton-override <kanton>- Setzt den Wohnkanton aller Fälle auf einen festen Wert.
Beim Start meldet der Service den Hörsocket auf der Standardausgabe. Beendet wird er mit Ctrl+C.
Requests
Sowohl Eingabe als auch Ausgabe der Requests erfolgen
grundsätzlich als JSON. Die Ausgabe ist nicht formatiert; in den
Beispielen ist sie zur besseren Lesbarkeit mit jq
formatiert.
GET /version
Liefert die Version des Service.
> curl -s http://127.0.0.1:8080/version | jq .
{
"status": 200,
"text": "OK",
"result": {
"version": "2026.2.1"
}
}
GET /healthz
Liveness-Check für Monitoring und Reverse Proxies. Antwortet
immer mit 200 OK, ohne die Konfiguration oder das
Dateisystem zu berühren — geeignet für häufige Aufrufe durch
systemd, nginx oder externe Health-Checks.
> curl -s http://127.0.0.1:8080/healthz | jq .
{
"status": "ok"
}
POST /group/<Release>
Gruppiert die übermittelten Falldaten mit der angegebenen SPLG-Definition und liefert das Resultat zurück.
> 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"]
}
}
}
Auch wenn nur eine einzige Behandlung definiert ist, wird der
Fall erfolgreich gruppiert und die SPLG BEW7.1.1
ausgegeben.
Antwortstruktur
Jede Antwort des Service ist ein JSON-Objekt mit drei Feldern:
status(Zahl)- Numerischer Statuscode, gespiegelt aus dem
HTTP-Statuscode (z. B.
200bei Erfolg). text(String)- Kurze textuelle Beschreibung des Status (z. B.
"OK"oder eine Fehlermeldung). result(Objekt, optional)- Bei Erfolg das eigentliche Ergebnis. Bei Fehlern weggelassen.
Bei einer erfolgreichen Gruppierung enthält
result die folgenden Felder:
splg(String)- Die für den Fall ermittelte Spitalplanungs-Leistungsgruppe. Leerer String, falls keine SPLG zugeordnet werden konnte.
quer(Array von Strings)- Querschnittsleistungsgruppen, denen der Fall zusätzlich zugeordnet wird.
mfzs(Array von Strings)- MFZS-Leistungsgruppen (Mindestfallzahlen Spital), denen der Fall zugeordnet wird.
mfzo(Array von Strings)- MFZO-Leistungsgruppen (Mindestfallzahlen Operateur), denen der Fall zugeordnet wird.
points(Array von Objekten)- Operateurspunkte. Jeder Eintrag enthält
gln(GLN des Operateurs),splg(zugehörige SPLG) undpoints(numerische Punktzahl). lactrl(Zahl)- Leistungscontrolling-Status (z. B.
0für "Leistungsauftrag vorhanden",99für "Leistungsauftrag unbekannt oder Fall nicht gruppierbar"). Vollständige Code-Liste: LACTRL. lactrlcodes(Array von Strings)- Codes, die zur Lactrl-Beurteilung beigetragen haben.
notes(String)- Zusätzliche Hinweise zur Gruppierung; oft leer.
errorcode(String)- Gruppierungsstatus (z. B.
"0"für eine fehlerfreie Gruppierung,"w31"für die Warnung "Hauptdiagnose fehlt"). Vollständige Code-Liste: ERROR. Bei mehreren Codes wird derjenige mit der höchsten Priorität ausgewiesen. configuration(Objekt)- Dokumentation der tatsächlich verwendeten Konfiguration (siehe unten).
Der Block configuration hilft beim
Nachvollziehen, mit welcher Definition und welcher Spitalliste
ein konkretes Resultat zustande gekommen ist. Das ist nützlich,
wenn der Service mehrere Releases bedient oder wenn sich
Definitionen oder Spitallisten zwischen Service-Läufen
unterscheiden:
defversion(String)- Versionsbezeichnung der verwendeten SPLG-Definition
(z. B.
"A_2026_3"). Damit lassen sich Resultate eindeutig auf eine bestimmte Definition zurückführen. spitallisten(Array von Strings)- Versionsbezeichnungen der bei der Gruppierung verwendeten Spitallisten. Werden mehrere Spitallisten kombiniert, erscheinen entsprechend mehrere Einträge.
Fehlerantworten
400 Bad Request- Body fehlt oder ist kein gültiges JSON.
403 Forbidden- Definition vorhanden, aber Lizenz fehlt oder ist für den Release nicht gültig.
404 Not Found- Release nicht installiert (es existiert kein passender Unterordner im Konfigurationsordner) oder unbekannte URL.
413 Payload Too Large- Der Request-Body überschreitet die mit
--max-body-byteskonfigurierte Grenze. 500 Internal Server Error- Unerwarteter Fehler bei der Gruppierung. Der Servicelauf bleibt davon unberührt.
503 Service Unavailable- Der Service ist ausgelastet (alle
--max-threadsWorker beschäftigt). Die Anfrage kann später erneut versucht werden.
Falldaten-JSON
Die zu gruppierenden Falldaten werden als JSON dem Service
übermittelt. Die Struktur entspricht der bestehenden
SPLG-JSON-Datei
(ohne den umschliessenden splg-json-Container) und
basiert auf dem
abstrakten Eingabeformat.
{
"burnr": "12345678",
"agey": "45",
"aged": "0",
"austritt": "20230227",
... weitere Felder gemäss abstraktem Format ...
"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"
}
]
}
Alle Felder sind optional; damit gruppiert werden kann, müssen
jedoch die für die Gruppierung relevanten Angaben (Diagnosen,
Behandlungen, Alter usw.) vorhanden sein. Bis auf die technischen
rang-Felder enthalten alle Felder String-Werte; leere
Felder können auch ganz weggelassen werden. Anstelle von
bewegungen wird auch patientenbewegungen
akzeptiert, um mit dem bestehenden SPLG-JSON-Dateiformat
kompatibel zu bleiben.
Deployment unter Linux (systemd + nginx)
Für einen dauerhaften Betrieb empfiehlt sich, den Service als systemd-Unit zu betreiben und ihm einen nginx-Reverse-Proxy vorzuschalten. systemd übernimmt Start, Neustart bei Absturz und saubere Beendigung; nginx übernimmt TLS, Rate-Limiting, Access-Log und ggf. Authentifizierung — Aufgaben, die nicht im Service selbst implementiert sind.
systemd-Unit
Eine minimale Unit-Datei (anzupassen an Pfade und Benutzer):
[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
Bemerkungen:
--host 127.0.0.1stellt sicher, dass der Service nur lokal erreichbar ist; die Aussenwelt erreicht ihn ausschliesslich über nginx.TimeoutStopSecsollte mindestens so gross sein wie--graceful-stop-ms, damit laufende Anfragen beisystemctl stopsauber abgeschlossen werden können.RuntimeMaxSec=24hstartet den Service einmal pro Tag automatisch neu — als günstige Versicherung gegen schleichendes Speicherwachstum bei langer Laufzeit.- Die Konfiguration installiert sich nach
/etc/systemd/system/splg-grouper.service; danach aktivieren mitsystemctl daemon-reloadundsystemctl enable --now splg-grouper.
nginx-Reverse-Proxy
Vereinfachter nginx-Server-Block für die TLS-Terminierung und das Weiterreichen an den lokalen Service:
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;
}
Wichtig:
client_max_body_sizesollte mit--max-body-bytesübereinstimmen; nginx weist übergrosse Anfragen dann bereits vor dem Backend ab./healthzwird ohne Rate-Limit weitergereicht, damit Monitoring den Service jederzeit prüfen kann.- Soll der Zugriff zusätzlich abgesichert werden, kann z. B.
eine IP-Allowlist (
allow/deny) oder ein gemeinsamer API-Schlüssel im Header eingerichtet werden — sinnvoll, da der Service selbst keine Authentifizierung kennt.