newTransactionId static method

String newTransactionId({
  1. int? nowMillis,
})

Eine eindeutige, numerische Kennung mit hoechstens 18 Stellen: 13 Stellen Zeitstempel in Millisekunden, dazu ein 5-stelliger Zaehler je Millisekunde (statt Zufall). Der Zeitstempel allein reichte nicht -- zwei Vorgaenge in derselben Millisekunde bekamen dieselbe Kennung, und ein Zufallsanteil kann (selten, aber nachweislich geschehen) genauso kollidieren.

Die Zaehlung ist eine logische Uhr, angelehnt an das Snowflake-Verfahren: die zuletzt vergebene Millisekunde _lastTransactionMs laeuft niemals rueckwaerts. Liefert die Systemuhr (oder das injizierte nowMillis) keinen groesseren Wert als beim letzten Aufruf -- egal ob wegen derselben Millisekunde oder einer zurueckspringenden Uhr (Zeitumstellung, NTP-Korrektur) -- bleibt die Millisekunde stehen und nur der Zaehler steigt. Ist der Zaehler einer Millisekunde ausgeschoepft (100000 Kennungen), schaltet die Millisekunde gedanklich um eins weiter und der Zaehler beginnt neu bei 0; dieser Sprung nach vorn wird zur neuen Referenz, gegen die weiter verglichen wird. So liefert diese Methode innerhalb EINES Prozesses garantiert nie zweimal dieselbe Kennung -- unabhaengig von Aufrufrate oder Uhrsprung.

Diese Garantie gilt ausdruecklich nur innerhalb des einen Prozesses, der den statischen Zustand haelt: zwei getrennte Prozesse oder Geraete, die in derselben Millisekunde eine Kennung bilden, koennen weiterhin kollidieren, weil sie voneinander nichts wissen. Fuer den Einsatz hier passt das: die App laeuft je Terminal in genau einem Prozess.

Oeffentlich, damit ein Aufrufer die Kennung VOR dem Request festlegen kann -- ohne das ist sie nach einem Abbruch verloren, und damit sind Statusabfrage und Storno unerreichbar.

nowMillis ersetzt die Systemuhr; ausschliesslich fuer Tests gedacht, die eine feste oder rueckwaerts springende Zeit erzwingen wollen.

Implementation

static String newTransactionId({int? nowMillis}) {
  final now = nowMillis ?? DateTime.now().millisecondsSinceEpoch;
  final lastMs = _lastTransactionMs;
  int ms;
  if (lastMs == null || now > lastMs) {
    ms = now;
    _transactionCounter = 0;
  } else {
    ms = lastMs;
    _transactionCounter++;
    if (_transactionCounter >= 100000) {
      ms++;
      _transactionCounter = 0;
    }
  }
  _lastTransactionMs = ms;
  final suffix = _transactionCounter.toString().padLeft(5, '0');
  return '$ms$suffix';
}