O projekcie
System, który nadaje klientowi sklepu poziom — brąz, srebro, złoto — i utrzymuje go w czasie na podstawie tego, ile naprawdę u tego sklepu zostawił. Poziom nie jest odznaką: steruje rabatem, progiem darmowej wysyłki, kolejnością w obsłudze i dostępem do przedsprzedaży. Każde przesunięcie w górę i w dół jest więc decyzją o pieniądzach, a nie o etykiecie.
Trudną częścią nie są poziomy. Trudną częścią jest pytanie, KTO jest klientem. Sklep widzi zamówienia, a nie ludzi: ten sam człowiek kupuje raz na konto, raz jako gość, raz ze skrzynki służbowej, a przy trzeciej przeprowadzce zakłada nowe konto, bo nie pamięta hasła. Historia jednej osoby leży wtedy w czterech kawałkach, a poziom liczy się z jednego z nich.
Dlatego rdzeniem tego projektu jest ROZSTRZYGANIE TOŻSAMOŚCI, a warstwa poziomów siedzi dopiero nad nim. Kandydatów na to samo konto proponuje model; łączy je człowiek albo reguła o wysokiej pewności; a sam poziom wylicza kod deterministyczny, którego wynik da się powtórzyć co do grosza. Ten podział nie jest ostrożnością — wynika z tego, że próg to pieniądze, a model probabilistyczny nie ma prawa rozdawać cudzych rabatów.
Adres jest tu drugim sygnałem, nie drugim kluczem. Ta sama ulica zapisana przez dwie osoby wygląda inaczej — raz „ul.”, raz „ulica”, raz z numerem lokalu po ukośniku, raz po przecinku, raz z literówką w nazwie miasta. Do tego jeden adres bywa wspólny: akademik, biuro, paczkomat. Adres potwierdza więc parę zaproponowaną przez inny sygnał i sam z siebie nikogo nie łączy.
Model jest tu potrzebny do jednej rzeczy i tylko do niej: do wskazania PAR KANDYDATÓW. Porównanie każdego konta z każdym rośnie kwadratowo i przy kilkudziesięciu tysiącach klientów przestaje być wykonalne, więc najpierw blokowanie po tanich kluczach zawęża pole, a dopiero potem model ocenia to, co zostało. Sama reguła by tu nie wystarczyła: napisana ostrożnie nie łączy prawie niczego i program lojalnościowy dalej kłamie, napisana odważnie skleja obcych ludzi. Model daje to, czego reguła nie ma — stopień pewności, na którym da się postawić próg.
Kolejka propozycji do zatwierdzenia jest miarą zdrowia całego systemu. Jeśli rośnie, ludzie zaczynają ją przeklikiwać, a przeklikana kolejka jest gorsza niż jej brak, bo wygląda na kontrolę. Dlatego progi są dobrane tak, żeby do człowieka trafiały wyłącznie przypadki naprawdę niejednoznaczne — reszta idzie automatem albo odpada — a wielkość kolejki jest mierzona i traktowana jak usterka, nie jak praca do wykonania.
Poziom jest obietnicą złożoną klientowi, więc sklep nie może się z niej wycofać po cichu. Zmiana progów obowiązuje od daty, nie wstecz; przeliczenie historii według nowych reguł jest osobną, świadomą decyzją, a nie skutkiem ubocznym wdrożenia. Ta sama zasada działa w drugą stronę przy scaleniu: człowiek, któremu dopisano cudzą historię, nie traci poziomu w chwili rozdzielenia profili — dostaje ten sam okres ochronny co przy zwykłym spadku.
Całość jest zbudowana tak, żeby dała się cofnąć i wytłumaczyć. Scalenie dwóch profili to przeniesienie cudzej historii zakupów do czyjegoś konta — pomyłka w tym miejscu jest wyciekiem danych, nie usterką porządkową. Rozdzielenie musi być więc wykonalne w jednym ruchu i musi zostawić ślad, a każda zmiana poziomu zapisuje powód i wersję reguł, według której zapadła.
Problem
Sklep miał program lojalnościowy liczony po adresie e-mail: suma zamówień z danej skrzynki decydowała o progu. Dawało to dwa błędy naraz i oba kosztowne. Stali klienci kupujący z kilku adresów albo raz jako goście nie dobijali do progu, choć przekraczali go wielokrotnie — i odchodzili przekonani, że program jest fikcją. Jednocześnie skrzynki współdzielone w firmach kumulowały zakupy kilku osób i wskakiwały na najwyższy poziom, rozdając rabat każdemu, kto akurat znał hasło. Nikt nie umiał też odpowiedzieć na najczęstsze pytanie obsługi: dlaczego ten klient stracił poziom.
Rozwiązanie
Warstwa tożsamości oddzielona od warstwy poziomów. Osobna encja „osoba” zbiera pod sobą konta, e-maile i adresy; model podpowiada, które z nich są tym samym człowiekiem, a próg pewności rozstrzyga, czy scalenie idzie automatem, czy do kolejki obsługi. Na to nakłada się silnik reguł, który z zamówień ROZLICZONYCH — po odjęciu zwrotów i anulowań — liczy obrót w oknie przesuwnym i wybiera poziom. Spadek nie następuje z dnia na dzień: klient dostaje zapowiedź i okno na jej odrobienie. Każda decyzja ląduje w dzienniku, z którego obsługa czyta powód na ekranie, zamiast zgadywać.
Funkcje
- Osoba jako byt nadrzędny nad kontem: jedna osoba może mieć wiele kont, e-maili i adresów, a poziom należy do osoby, nie do skrzynki.
- Dopasowanie tożsamości z progiem pewności: powyżej progu scalenie idzie automatem, w paśmie niepewności trafia do kolejki, poniżej jest odrzucane bez śladu w profilu.
- Adres jako POTWIERDZENIE pary, nigdy jako samodzielny klucz — z rozpoznawaniem adresów zbiorowych, które nie potwierdzają niczego.
- Poziom liczony deterministycznie: ten sam zestaw zamówień zawsze daje ten sam wynik, co do grosza i niezależnie od kolejności przetwarzania.
- Obrót z zamówień rozliczonych: zwrot, anulowanie i obciążenie zwrotne odejmują, więc karuzela „kup i zwróć” nie buduje poziomu.
- Okno przesuwne zamiast roku kalendarzowego, żeby zakup z grudnia nie przepadał klientowi 1 stycznia.
- Degradacja z zapowiedzią: klient widzi, ile brakuje i do kiedy, zanim poziom spadnie.
- Rozdzielenie błędnie scalonych profili w jednym ruchu, z przywróceniem historii po obu stronach i wpisem w dzienniku.
- Dziennik decyzji tylko do dopisywania: każda zmiana poziomu z powodem, wejściem i wersją reguł, po której da się odtworzyć stan sprzed roku.
- Ekran obsługi odpowiadający na pytanie „dlaczego ten klient ma ten poziom” bez zaglądania do bazy.
- Obsługa żądań RODO: wgląd, sprostowanie i sprzeciw wobec profilowania działają na osobie, a nie na pojedynczym koncie.
Moduły
normalizator-adresow
usługaSprowadza adres do postaci, którą da się porównywać, i mówi wprost, czego nie umiał rozpoznać.
- Rozwija skróty („ul.”, „al.”, „os.”) i ujednolica zapis numeru domu i lokalu.
- Poprawia kod pocztowy i miasto wobec siebie; rozjazd oznacza jako niepewny zamiast zgadywać.
- Zwraca postać kanoniczną ORAZ ocenę jakości — adres słabo rozpoznany nie ma prawa potwierdzać scalenia.
- Nie odrzuca adresów nietypowych: paczkomat i skrytka pocztowa dostają własny typ, bo są adresami wysyłki, nie zamieszkania.
wykrywacz-adresow-zbiorowych
usługaRozpoznaje adresy, pod którymi mieszka wiele niepowiązanych osób, i odbiera im moc potwierdzania.
- Liczy, ile różnych nazwisk i kont kupuje pod tym samym adresem kanonicznym.
- Powyżej progu adres przestaje potwierdzać pary, a zaczyna je OSŁABIAĆ — akademik i biurowiec nie są dowodem na nic.
- Paczkomaty i punkty odbioru wpadają tu z definicji, bez czekania na statystykę.
- Lista jest wyliczana, nie wpisywana ręcznie, więc nowy biurowiec rozpoznaje się sam.
dopasowanie-tozsamosci
model + regułyProponuje pary kont, które prawdopodobnie są tym samym człowiekiem, i podaje, z czego to wnioskuje.
- Sygnały: zbieżność nazwiska, adres kanoniczny, numer telefonu, końcówka karty, urządzenie i historia wysyłki.
- Każda para dostaje wynik liczbowy i ROZPISKĘ sygnałów — obsługa widzi powód, nie samą liczbę.
- Trzy pasma: scalenie automatyczne, kolejka do człowieka, odrzucenie. Progi są konfigurowalne i wersjonowane.
- Model nigdy nie zapisuje poziomu ani rabatu; jego jedynym wyjściem jest propozycja scalenia.
scalarka-profili
usługaWykonuje scalenie i rozdzielenie osób w transakcji, zawsze odwracalnie.
- Scalenie przenosi zamówienia pod osobę, zostawiając konta rozłączne — konta nie znikają, tylko zyskują wspólnego właściciela.
- Rozdzielenie odtwarza stan sprzed scalenia z dziennika, nie z domysłu, i przelicza poziom po obu stronach.
- Każda operacja zapisuje autora: reguła automatyczna albo konkretny pracownik.
- Scalenia nie da się wykonać dwa razy ani w dwóch kierunkach naraz — klucz w bazie tego pilnuje, nie kolejność wywołań.
silnik-poziomow
silnik regułZ obrotu osoby wylicza poziom w sposób powtarzalny i wytłumaczalny.
- Wejściem są wyłącznie zamówienia rozliczone: opłacone, pomniejszone o zwroty, anulowania i obciążenia zwrotne.
- Kwoty liczone w groszach i w jednej walucie; przeliczenie kursem z dnia rozliczenia, nie z dnia wyliczania.
- Reguły są danymi, nie kodem — zmiana progu jest wpisem z datą obowiązywania, a nie wdrożeniem.
- Wynik zawiera nie tylko poziom, ale i odległość do następnego progu oraz datę, po której obrót wypadnie z okna.
okno-obrotu
bibliotekaLiczy obrót w oknie przesuwnym, zamiast ścinać historię na przełomie roku.
- Okno przesuwne (domyślnie dwanaście miesięcy) liczone od dnia rozliczenia, nie od dnia złożenia zamówienia.
- Wypadnięcie zakupu z okna jest zdarzeniem planowanym, więc spadek poziomu daje się zapowiedzieć z wyprzedzeniem.
- Czysta funkcja bez dostępu do bazy — dzięki temu ma testy na przypadki graniczne zamiast na całą aplikację.
- Strefa czasowa sklepu jest jawnym parametrem; zamówienie z 23:50 nie zmienia dnia zależnie od serwera.
zapowiedz-spadku
usługaOstrzega klienta przed utratą poziomu, zamiast konfrontować go z faktem.
- Wylicza dzień, w którym obrót spadnie poniżej progu, i wysyła zapowiedź z wyprzedzeniem.
- Pokazuje brakującą kwotę, a nie samą groźbę — informacja ma dawać wybór, nie wywoływać poczucie kary.
- Zapowiedź jest jedna na okres; ponowne uruchomienie przeliczenia nie powiela wiadomości.
- Sam spadek jest osobnym zdarzeniem z własnym wpisem, więc obsługa widzi, że klient został uprzedzony.
dziennik-decyzji
usługaZapis tylko do dopisywania: co, kiedy, na jakiej podstawie i według jakiej wersji reguł.
- Każdy wpis niesie wejście (obrót, okno, progi), wynik i identyfikator wersji reguł.
- Wpisów nie da się zmienić ani skasować — korekta jest nowym wpisem, nie nadpisaniem starego.
- Na podstawie dziennika da się odtworzyć poziom klienta na dowolny dzień wstecz.
- To ten sam dziennik obsługuje reklamacje i pytania z RODO, więc nie ma dwóch wersji prawdy.
wtyczka-sklepu
wtyczka WooCommercePokazuje poziom tam, gdzie klient podejmuje decyzję, i pilnuje, żeby rabat liczył się raz.
- Poziom i odległość do progu widoczne na koncie klienta oraz w koszyku, przed przejściem do kasy.
- Rabat wynikający z poziomu nie sumuje się po cichu z kuponem — reguła pierwszeństwa jest jawna.
- Wtyczka nie liczy poziomu sama; pyta o niego usługę i pokazuje to, co dostała.
- Przy niedostępnej usłudze sklep sprzedaje dalej po cenie podstawowej, zamiast zatrzymywać zakup.
panel-obslugi
panelOdpowiada na pytanie „dlaczego ten klient ma ten poziom” bez zaglądania do bazy.
- Oś czasu osoby: zakupy, zwroty, scalenia, zmiany poziomu i wysłane zapowiedzi w jednym miejscu.
- Kolejka propozycji scalenia z rozpiską sygnałów i dwoma przyciskami: połącz albo odrzuć.
- Podgląd skutku PRZED zatwierdzeniem: jaki poziom wyjdzie po scaleniu i o ile zmieni się obrót.
- Dostęp do danych adresowych za osobnym uprawnieniem, z zapisem każdego wglądu.
Architektura
- Usługa
- Python (FastAPI), zadania w tle na kolejce, wdrożenie kontenerowe
- Baza
- PostgreSQL — osoby, konta, adresy kanoniczne, zamówienia rozliczone, dziennik decyzji
- Model dopasowania
- Gradient boosting na cechach par; wynik z rozpiską udziału sygnałów
- Progi pewności
- Trzy pasma (automat / kolejka / odrzucenie), wersjonowane razem z regułami
- Normalizacja adresów
- Słownik skrótów, walidacja kodu wobec miasta, ocena jakości dopasowania
- Reguły poziomów
- Dane, nie kod: progi z datą obowiązywania, przeliczenie wstecz bez wdrożenia
- Waluta i kwoty
- Grosze jako liczby całkowite, jedna waluta rozliczeniowa, kurs z dnia rozliczenia
- Okno obrotu
- Przesuwne dwanaście miesięcy od dnia rozliczenia, strefa czasowa jako parametr
- Integracja ze sklepem
- Wtyczka WooCommerce + REST; sklep pyta o poziom, nigdy go nie liczy
- Pamięć podręczna
- Redis na odczyt poziomu w koszyku; unieważniana zdarzeniem, nie czasem
- Dane osobowe
- Minimalizacja i retencja; wgląd, sprostowanie i sprzeciw wobec profilowania na poziomie osoby
- Bezpieczeństwo
- Dostęp do adresów za osobnym uprawnieniem, każdy wgląd zapisany; dziennik tylko do dopisywania
- Testy
- Przypadki graniczne okna i progów jako czyste funkcje; scenariusze scalenia i rozdzielenia na danych syntetycznych
Technologie
- Python
- PostgreSQL
- REST API
- TypeScript
- WooCommerce
- Redis
Autor
Mariusz PerzyńskiClient Relations & Operations