Dokumen ini memetakan perilaku current DemografiEngine Terminal dari semua intake data sampai persistensi, pencarian, pemakaian rule, dan scoring. Ini adalah baseline source saat ini, bukan desain target. Terminal memiliki subtype ATM, POS, dan ONLINE, tetapi penentuan subtype saat deserialisasi dan saat penyimpanan mempunyai ketidaksesuaian yang harus diuji runtime.
| Area | Status | Batas verifikasi |
|---|---|---|
| REST, scheduler XML, command, search, cache | SOURCE-TRACED | Ditelusuri dari controller, service, entity, repository. |
| Trigger dan algoritme score terminal | SOURCE-TRACED | Ditelusuri dari RunAction, BaseAlertServiceImpl, ScoringService, evaluator. |
| Schema/seed/config | SOURCE-TRACED | Ditelusuri dari Liquibase dan application profile. |
| Polymorphic JSON/XML dan JOINED persistence subtype | NOT RUNTIME-VERIFIED | Tidak menjalankan deserialisasi ataupun insert subtype. |
| Path/cron/data production | NOT PRODUCTION-VERIFIED | Profile dan filesystem produksi tidak diperiksa. |
| Concern | Komponen current | Tanggung jawab |
|---|---|---|
| API | TerminalController |
POST /terminal-data/save dan /search. |
| File intake | TerminalServiceImpl.saveTerminalPath |
Cron memproses XML input directory. |
| Type resolution | TerminalTypeMapper dan @JsonTypeInfo di Terminal |
Menerjemahkan 1/ATM, 2/POS, 3/ONLINE; lainnya GENERAL. |
| Persist | TerminalRepository dan tiga subtype repository |
Menulis data_terminal serta table subtype JOINED. |
| Rule/cache | TerminalCache, ResolverTypeTerminal |
Membaca data_terminal dengan cache 30 menit. |
| Scoring | ScoringService, ScoringEvaluatorService |
Menulis score dan scoringLevel. |
Ada dua cara aktif untuk memasukkan data terminal. REST menerima TerminalApps; scheduler @Scheduled(cron = "${demographic.terminal.cron.save}") membaca file .xml lalu memakai XmlMapper ke DTO yang sama.
| Jalur | Input | Outcome current |
|---|---|---|
| REST | JSON list application |
String aggregate Processed/Created/Updated/Ignored/Failed; controller memberi HTTP 200 bila per-record error tertangkap. |
| Scheduler | XML list application |
File corrupt dipindahkan ke error; error record ditulis sebagai file isolasi; source batch dipindah output bahkan bila ada record gagal. |
[!DANGER]
saveTerminalDatamengiterasidata.getApplication()tanpa null check.application=nullmenghasilkan exception controller/HTTP 500.
[!DANGER] Variabel
terminalIddi jalur REST selalu"UNKNOWN"pada catch; log error REST tidak menampilkan id terminal yang sebenarnya.
[!DANGER] Scheduler hanya menerima suffix
.xmlcase-sensitive. File.XMLtidak diproses.
TerminalTypeMapper.resolve menerima 1/ATM → ATM, 2/POS → POS, 3/ONLINE → ONLINE, dan semua nilai lain/null → GENERAL. Lalu processSingleTerminal melakukan cast subtype berdasarkan hasil ini dan memakai repository subtype. Setiap nilai terminal dibersihkan ServiceHelper.sanitize(newValue, 255).
Raw terminalType |
Resolved service type | Expected Java class/repository | Table hierarchy |
|---|---|---|---|
1, ATM |
ATM | TerminalATM / TerminalAtmRepository |
data_terminal + data_terminal_atm |
2, POS |
POS | TerminalPos / TerminalPosRepository |
data_terminal + data_terminal_pos |
3, ONLINE |
ONLINE | TerminalOnline / TerminalOnlineRepository |
data_terminal + data_terminal_online |
| null/other | GENERAL | Terminal / TerminalRepository |
data_terminal |
| Command | Data ditemukan pada repository tipe yang dipilih | Current write | Cache |
|---|---|---|---|
CREX |
Tidak | Insert | Evict. |
CREX |
Ya | Tidak ada | Tidak berubah. |
CRUP |
Tidak | Insert | Evict. |
CRUP |
Ya | Full replacement payload memakai id lama | Evict. |
IGNR |
Apa pun | Tidak ada | Tidak ada. |
[!DANGER]
Terminal.terminalTypebertipeInteger, tetapi@JsonSubTypesjuga mendeklarasikan discriminator bernamaATM,POS,ONLINE. JSON/XML yang memakai string nama subtype dapat gagal memetakan keInteger; ini NOT RUNTIME-VERIFIED.
[!DANGER] Bila mapper memilih POS/ONLINE/ATM tetapi object hasil deserialisasi masih
Terminal(bukan subtype), cast eksplisit(TerminalPos|TerminalOnline|TerminalATM)diprocessSingleTerminalmelemparClassCastException. Error menjadi counter failure/isolation file, bukan fallback GENERAL.
[!DANGER] Field
terminal_typediberi@Column(insertable=false, updatable=false)walaupun juga discriminator JPA.TerminalTypeMapper.toDbValuehanya dipakai untuk filter search, bukan ketika persist. Source tidak secara eksplisit mengisi discriminator dari mapper; keberhasilan subtype insert bergantung Hibernate polymorphic persistence.
[!DANGER]
terminal_idhanya memiliki kolom biasa di Liquibase, bukan unique constraint. Lookup per subtype memungkinkan id sama berada di hierarchy/subtype berbeda dan scoring/cache selalu memakaiTerminalRepositorygeneral.
Search memilih class table berdasarkan request terminalType; setelah key ini dihapus dari filter body, predicate tidak membatasi type pada table terpilih. GENERAL adalah default saat parameter tak ada atau tidak dikenal.
[!DANGER]
TerminalCachehanya memakaiTerminalRepositorygeneral. Untuk entity subclass yang disimpan lewat JPA JOINED, apakah repository mengembalikan polymorphic subtype dapat bergantung runtime ORM; untuk terminal id duplicate lintas subtype hasilnya ambigu.
[!DANGER]
ordermenjadiroot.get(order)dan key filter default menjadiroot.get(key)tanpa allowlist. Field salah menghasilkan exception/HTTP 500.
[!DANGER]
installDatemerupakanVARCHAR(255)dan filter date mengubah ISOInstantke stringyyMMddHHmmssmemakai timezone sistem; source membandingkan string, bukan timestamp.
Input terminal tidak otomatis memanggil score engine. Ada tiga trigger score: action CREATE_SCORE, action UPDATE_SCORE, dan BaseAlertServiceImpl.triggerScoring untuk alert dengan alertStatus != AUTO_NEGATIVE serta isDuplicated == false.
| Trigger | Formula current | Catatan |
|---|---|---|
createScore |
clamp(initialScore, 0..100) |
Skip bila existing score > 0. |
updateScore |
clamp(previous + newScore, 0..100) |
Nama newScore bukan assignment absolut. |
incrementScore |
clamp(previous + getAutoIncrement(TERMINAL, previous), 0..100) |
Skip bila previous ≥100. |
| Level | First active threshold match ordered value1 DESC; else LOW |
Seed: LOW <40, MEDIUM 40..70, HIGH ≥70, increment 10. |
[!DANGER]
ScoringService.requireTerminalselalu memakai generalTerminalRepository.findByTerminalId, bukan repository subtype yang dipakai saat insert/update. Jika subtype lookup/polymorphism tidak konsisten, scoring dapat gagal walaupun record dimasukkan lewat subtype repository.
[!DANGER]
ScoringServicetidak memanggilterminalCache.invalidateCache, sehingga resolver rule dapat melihatscoreatauscoringLevelstale hingga TTL 30 menit.
[!DANGER] Exception
incrementScorepada alert ditangkapBaseAlertServiceImpl.tryScoredan hanya dilog; alert/case tetap dilanjutkan tanpa score terminal.
| Scenario | Persist/response | Cache/score | Next step |
|---|---|---|---|
| REST/scheduler CREX GENERAL baru | Insert data_terminal |
Cache evict; score hanya payload | Rule dapat read terminal. |
| CREX typed subtype baru | Expected JOINED save | Cache evict | Keberhasilan cast/persist runtime-verify. |
| CRUP typed existing | Full payload save old id | Cache evict | Subtype association/data dapat terganti. |
Unknown terminalType |
GENERAL repository | Tidak ada validation warning | Data bisa disimpan general. |
| Mismatch resolved type vs Java subtype | Item error | REST count failed / scheduler isolation XML | Perbaiki source format. |
| XML corrupt | Tidak ada DB write | Source XML moved error | Batch lanjut file lain. |
| Eligible alert terminal ada | Increment 10/default threshold | Cache tetap stale | Save score/level. |
| Eligible alert terminal tidak ada | Warning only | Alert proceeds | Tidak ada score. |
| UPDATE_SCORE +10 | Adds 10, not set 10 | Cache stale | Recompute level. |
| State | Authoritative source | Projection/consumer |
|---|---|---|
| Base profile & score | data_terminal |
General repository, cache, scoring. |
| ATM/POS/ONLINE fields | data_terminal_atm / _pos / _online |
Subtype repositories/search classes. |
| Identity | terminalId (tanpa unique constraint) |
Service/repositories/cache/score. |
| Type | JPA discriminator terminal_type; service raw mapper |
Deserialization, repository selection, search class selection. |
| Cache | CacheSupport key terminal:<id>, TTL 30 min |
ResolverTypeTerminal. |
| Threshold | Active T_SCORING_THRESHOLD_SETTING rows for TERMINAL |
Evaluator. |
| Batch file state | input/output/error configured paths | Scheduler; no DB audit. |
| Gap current | Dampak |
|---|---|
| String subtype annotation vs Integer field | Valid ATM/POS/ONLINE payload bisa gagal deserialisasi/cast. |
| Mapper does not set DB discriminator | Service route and persisted type can diverge. |
| Non-unique terminal id | Repository/cache/scoring result ambiguous. |
| Score uses base repository only | Subtype persistence can be invisible/inconsistent to score lookup. |
| No score during intake | New terminal tidak otomatis dinormalisasi score/level. |
| Score update does not invalidate cache | Rule dapat memakai score stale. |
| Dynamic search order/filter | Invalid client key produces HTTP 500. |
| Per-record REST failure returns 200 | Caller tidak menerima structured item errors. |
| Surface | Endpoint family | Capability |
|---|---|---|
| Terminal intake | POST /terminal-data/save |
Batch command CREX/CRUP/IGNR. |
| Terminal read | POST /terminal-data/search |
Search selected type, pagination, sort. |
| Score threshold | /scoring-threshold/* |
Shared threshold update/read, including TERMINAL. |
| File intake | ${demographic.terminal.cron.save} |
XML polling; bukan REST endpoint. |
| Flow/Area | Primary source |
|---|---|
| Controller/service | DemografiEngine/Terminal/TerminalController.java; DemografiEngine/Terminal/TerminalServiceImpl.java |
| DTO/type/domain | DemografiEngine/Application/Terminal/TerminalApp.java; DemografiEngine/Application/Terminal/TerminalApps.java; DemografiEngine/Domain/Terminal*.java |
| Repositories/cache/rule | DemografiEngine/Terminal/TerminalRepository.java; DemografiEngine/Terminal/Repository/Terminal*Repository.java; DemografiEngine/Cache/TerminalCache.java; Rule/Engine/CoreNew/Processor/Resolver/Component/NonAggregate/Type/Component/ResolverTypeTerminal.java |
| Score | DemografiEngine/Scoring/ScoringService.java; DemografiEngine/Scoring/ScoringEvaluatorService.java; AlertManagement/Resource/ActionRequest/RunAction.java; AlertManagement/Abstract/BaseAlertServiceImpl.java |
| Schema/seed | db/changelog/DataDemographic/20260409190000_alfin-wildan_create_data_terminal*.xml; db/changelog/DataDemographic/Seed/20260428000007_alfin-wildan_seed_scoring_threshold_setting.xml; db/changelog/DataDemographic/Seed/20260409000000_alfin-wildan_seed_terminal_demographic.xml |
1, ATM, 2, POS, 3, ONLINE, serta unknown/null terminal_type untuk membuktikan subtype actual.CRUP dan scoring untuk setiap subtype, lalu query TerminalRepository/cache untuk memastikan identity dan polymorphism konsisten.