Dokumen ini memetakan perilaku current DemografiEngine Account dari intake, persistensi, query, cache/rule consumer, sampai scoring. Ini baseline source saat ini, bukan desain target. Account dipakai melalui dua field transaksi (srcAccNumber dan destAccNumber), tetapi keduanya menunjuk ke source of truth dan score yang sama: data_account berdasarkan accountNumber.
| Area | Status | Batas verifikasi |
|---|---|---|
| REST/XML intake, command, DB/cache/search | SOURCE-TRACED | Controller, service, entity, repository ditelusuri. |
| Score action dan alert auto-increment | SOURCE-TRACED | Alert/action/score source ditelusuri. |
| Schema/threshold seed | SOURCE-TRACED | Liquibase ditelusuri. |
| Filesystem, cascade nested data, actual cron | NOT RUNTIME-VERIFIED | Tidak menjalankan aplikasi. |
| Production profile/data | NOT PRODUCTION-VERIFIED | Tidak memeriksa environment produksi. |
| Concern | Komponen current | Tanggung jawab |
|---|---|---|
| API | AccountController |
Save batch dan search. |
| Batch file | AccountServiceImpl.saveAccountPath |
Cron XML dan lifecycle file. |
| Command | processSingleAccount |
CREX/CRUP/IGNR dan sanitasi. |
| Entity | Account / data_account |
Account profile, nested relations, score/level. |
| Rule consumer | AccountCache, ResolverTypeSrcAccNumber, ResolverTypeDestAccNumber |
Mengambil profile untuk source/destination transaction. |
| Scoring | ScoringService / evaluator |
Semua entity type SOURCE_ACCOUNT, DESTINATION_ACCOUNT, atau ACCOUNT menuju Account. |
POST /account-data/save menerima JSON AccountApps.application[]. Per-record exception ditangkap di saveAccountData, menjadi counter failed, dan controller mengembalikan HTTP 200 dengan summary bila method selesai normal.
saveAccountPath memakai ${demographic.account.cron.save}, membuat folder jika belum ada, hanya mengambil .xml, mengisolasi record gagal sebagai <source>_FAILED_<account>.xml, dan memindahkan source valid ke output walaupun terdapat error item.
[!DANGER]
data.getApplication()tidak memiliki null check di REST maupun batch loop.application=nullmenyebabkan exception, bukan summary yang terstruktur.
[!DANGER] Variabel log
accountNumberpada REST tidak diisi sebelum catch sehingga selaluUNKNOWNsaat error.
[!DANGER]
IGNRdihitung sebagai success scheduler meski tidak melakukan persistensi.
Lookup menggunakan accountNumber; Liquibase dan entity sama-sama menyatakan unique constraint. Object disanitasi hingga panjang 255 dan mengandung ManyToOne(cascade=ALL) untuk Credit, Customer, Limit, dan Device.
| Command | Existing account | Current result | Cache |
|---|---|---|---|
CREX |
Absent | Insert | Tidak di-evict. |
CREX |
Present | Ignore | Tidak berubah. |
CRUP |
Absent | Insert | Tidak di-evict. |
CRUP |
Present | Full replacement menggunakan old id | Evict. |
IGNR |
Any | No write | No change. |
| State | Storage current |
|---|---|
| Identity | data_account.account_number; unique constraint uk_data_account_account_number. |
| Profile | currency/type/status, avalBalance BigDecimal, createDate String. |
| Nested relation | Credit, Customer, Limit, Device; seluruhnya cascade ALL. |
| Score | score, scoringLevel di data_account; input payload dapat membawa nilainya. |
[!DANGER] CRUP melakukan full replacement dan cascade ALL. Payload parsial dapat mengubah association Credit/Customer/Limit/Device karena tidak ada patch/merge field-level.
[!DANGER] Insert CREX/CRUP absent tidak mengevict cache. Jika CacheSupport menyimpan null result atau ada interleaving lookup, consumer bisa melihat stale state hingga 30 menit; behavior null cache NOT RUNTIME-VERIFIED.
[!DANGER] Tidak ada
@Transactionalpada service/batch method. Source class ini tidak menampakkan boundary transaksi untuk seluruh batch atau record.
POST /account-data/search menggunakan Criteria API. Rule engine memiliki dua resolver yang berbeda, tetapi keduanya menggunakan AccountCache dan lookup account yang sama: source account dari srcAccNumber, destination dari destAccNumber.
[!DANGER]
orderdan fallback key filter dipakai sebagairoot.get(...)tanpa allowlist; invalid field menjadi exception/HTTP 500.
[!DANGER]
createDateadalah string. Filter date mengubah ISO Instant keyyMMddHHmmssdengan timezone sistem dan lalu membandingkan string, bukan timestamp database.
Score Account tidak dipanggil oleh intake. RunAction dapat memanggil createScore/updateScore; di ScoringService, SOURCE_ACCOUNT dan DESTINATION_ACCOUNT keduanya masuk branch Account yang sama. Alert eligible memanggil incrementScore("ACCOUNT", srcAccNumber) dan lagi untuk destAccNumber bila keduanya nonblank.
| Entry | Formula | Special current behavior |
|---|---|---|
createScore |
clamp initial 0..100 | Skip bila existing score >0. |
updateScore |
clamp(previous + newScore) | newScore adalah delta. |
incrementScore |
clamp(previous + threshold autoIncrement) | Skip bila ≥100. |
| level | first active matching threshold, else LOW | Seed ACCOUNT LOW <40, MEDIUM 40..70, HIGH ≥70, increment 10. |
[!DANGER] Satu alert bisa increment account yang sama dua kali bila
srcAccNumberdandestAccNumberidentik, karenatriggerScoringtidak melakukan deduplication antar dua pemanggilan Account.
[!DANGER]
ScoringServicetidak mengevictAccountCache. Resolver rule dapat melihatscoreatauscoringLevelstale sampai TTL 30 menit.
[!DANGER]
tryScorepada alert menelan exception. Account tidak ditemukan tidak membatalkan pembuatan alert/case atau memberi response error ke caller.
[!DANGER] Threshold overlap/range invalid tidak dicegah oleh update threshold; evaluator memilih match pertama berurutan
value1 DESC.
| Scenario | Persist/result | Cache/score | Next step |
|---|---|---|---|
| REST CREX new | Insert account/cascades | No automatic score | Summary 200. |
| CREX existing | No write | Unchanged | Ignored. |
| CRUP existing | Full replacement | Cache evicted | Reload on rule lookup. |
| Invalid command | Item failure | No expected write | REST can still report 200. |
| XML corrupt | File error path | No data write | Continue. |
| XML partial error | Isolation XML | Parent output | Manual retry failure. |
| Alert source and dest distinct | Two increments | Both scores saved | Level recalculated. |
| Alert source equals dest | Two increments same Account | Can add twice | Score may rise by 20 seed default. |
| Unknown account during score | IllegalStateException caught by alert | No score | Alert continues. |
| State | Authoritative source | Projection/consumer |
|---|---|---|
| Account profile | data_account |
REST, cache, rule and scoring. |
| Identity | Unique account_number |
All Account lookup pathways. |
| Nested profile | Credit/Customer/Limit/Device tables | Cascade from Account payload. |
| Cache projection | account:<accountNumber>, TTL 30m |
src/dest rule resolvers. |
| Score/level | data_account.score, scoring_level |
Scoring; cache consumer may lag. |
| Threshold | Active ACCOUNT settings | Evaluator. |
| File lifecycle | configured account paths | Scheduled intake. |
| Gap current | Dampak |
|---|---|
| Intake does not call scoring | Account baru tak otomatis mendapatkan normalized level. |
| Same src/dest double increment | Self-transfer dapat mengerek score dua kali per alert. |
| Full CRUP cascade replacement | Payload partial dapat mutasi nested records. |
| Cache unchanged after inserts/score | Rule can read stale profile/score. |
| REST success for partial failure | Client must parse summary string. |
| Date-as-string filtering | Timezone/lexical date issue. |
| Dynamic search fields/order | Invalid input returns 500. |
| Threshold ambiguity | Overlap determines evaluation by sorting behavior. |
| Surface | Endpoint family | Capability |
|---|---|---|
| Account intake | POST /account-data/save |
CREX/CRUP/IGNR batch. |
| Account search | POST /account-data/search |
Filter/pagination/order. |
| Threshold | /scoring-threshold/* |
Shared ACCOUNT threshold management. |
| XML intake | ${demographic.account.cron.save} |
Scheduled XML processing. |
| Flow/Area | Primary source |
|---|---|
| Account API/intake/search | DemografiEngine/Account/AccountController.java; DemografiEngine/Account/AccountServiceImpl.java |
| Model/cache | DemografiEngine/Domain/Account.java; DemografiEngine/Application/Account/AccountApp.java; DemografiEngine/Application/Account/AccountApps.java; DemografiEngine/Account/AccountRepository.java; DemografiEngine/Cache/AccountCache.java |
| Rule consumers | Rule/Engine/CoreNew/Processor/Resolver/Component/NonAggregate/Type/Component/ResolverTypeSrcAccNumber.java; ResolverTypeDestAccNumber.java |
| Score | AlertManagement/Resource/ActionRequest/RunAction.java; AlertManagement/Abstract/BaseAlertServiceImpl.java; DemografiEngine/Scoring/ScoringService.java; ScoringEvaluatorService.java |
| Schema/seed | db/changelog/DataDemographic/20260406100017_fadhiilabiyyi_create_data_account.xml; db/changelog/DataDemographic/Seed/20260401165730_alfin-wildan_seed_account-demographic.xml; db/changelog/DataDemographic/Seed/20260428000007_alfin-wildan_seed_scoring_threshold_setting.xml |