Wie verbessern Sie die Architektur eines Transferüberwachungsdienstes mit dem Observer -Muster im Spring Boot?
Posted: 29 Apr 2025, 13:12
Ich entwickle ein System zur Überwachung von Geldtransfers.).
In Zukunft werden immer mehr Kriterien hinzugefügt, und für jedes neue Kriterium wird
ein neuer Beobachter erstellt, um seine Logik unabhängig voneinander zu verkürzen. Übersicht: < /strong> < /p>
[*]: Hauptdienst, der dort baut, wo Bedingungen von Beobachtern und Monitoren übertragen werden.
[*]: Schnittstelle zum Definieren dynamischer Bedingungen.
[*]: Eine Beispielimplementierung (Überwachung von Übertragungen in FATF-gelistete Länder).
[*]: Sendet ausgewählte Transfers asynchron mit @aSync .
[*]: Ermöglicht das manuelle Senden einer einzelnen Übertragung durch seine ID.)
[*] Oracle -Datenbank (, clientdto , markRequestDTO )
[*] Repositories
(, clientRepository ) werden über Standard
implementiert
Code: Select all
Markaz
In Zukunft werden immer mehr Kriterien hinzugefügt, und für jedes neue Kriterium wird
ein neuer Beobachter erstellt, um seine Logik unabhängig voneinander zu verkürzen. Übersicht: < /strong> < /p>
[*]
Code: Select all
TransferMonitoringService
[*]
Code: Select all
TransferObserver
[*]
Code: Select all
FatfCountryObserver
[*]
Code: Select all
MarkService
[*]
Code: Select all
TdiController
Code: Select all
@Scheduled< /code>) < /li>
Spring Async (@aSync +
ThreadPoolTaskExecutor
[*] Oracle -Datenbank (
Code: Select all
JdbcTemplate< /Code>) < /li>
dtos
(TransferDto
[*] Repositories
(
Code: Select all
TransferRepository
implementiert
Code: Select all
JdbcTemplate< /code>, aber ich habe sie hier nicht hinzugefügt, um das Beispiel
short.
< /ul>
Bei Bedarf den vollständigen Code über Github-Gist oder
similar.
< /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> < /p> zu [url=viewtopic.php?t=13628]teilen[/url] PrettyPrint-Override ">@Data
public class TransferDto {...}
@Data
public class ClientDto{...}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonPropertyOrder({...})
pulic class MarkRequestDto{...}
//------------------------------------
public interface TransferObserver {
String getSqlCondition();
List getSqlParameters();
}
//------------------------------------
@Component
public class FatfCountryObserver implements TransferObserver {
private final JdbcTemplate jdbcTemplate;
@Autowired
public FatfCountryObserver(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public String getSqlCondition() {
List fatfCountries = getFatfCountries();
if (fatfCountries.isEmpty()) {
return null;
}
return "destination_country IN (" + fatfCountries.stream().map(c -> "?").collect(Collectors.joining(", ")) + ")";
}
@Override
public List getSqlParameters() {
return new ArrayList(getFatfCountries());
}
private List getFatfCountries() {
String sql = "SELECT kod FROM fatf_strn_list WHERE status = 1";
return jdbcTemplate.queryForList(sql, String.class);
}
}
//------------------------------------
@Slf4j
@Service
@RequiredArgsConstructor
public class TransferMonitoringService {
private final List observers;
private final TransferRepository transferRepository;
private final ClientRepository clientRepository;
private final MarkService markazService;
private final MarkRequestBuilderService markRequestBuilderService;
public void monitorNewTransfers() {
List whereParts = new ArrayList();
List allParams = new ArrayList();
for (TransferObserver observer : observers) {
String condition = observer.getSqlCondition();
if (condition != null) {
whereParts.add("(" + condition + ")");
allParams.addAll(observer.getSqlParameters());
}
}
if (whereParts.isEmpty()) {
log.warn("No monitoring conditions found.");
return;
}
String whereClause = String.join(" OR ", whereParts);
List newTransfers = transferRepository.findNewTransfers(whereClause, allParams);
for (TransferDto transfer : newTransfers) {
ClientDto client = clientRepository.findById(transfer.getClientId());
MarkRequestDto request = markRequestBuilderService.build(transfer, client);
markService.sendSpo(request);
}
}
}
//------------------------------------
@Slf4j
@Service
@RequiredArgsConstructor
public class MarkService {
private final Send2Mark send2Mark;
private final ObjectMapper objectMapper;
private static final String TDI_CREATE_PATH = "/tdi/create";
@Async("asyncExecutor")
public void sendSpo(MarkRequestDto requestDto) {
try {
String requestJson = objectMapper.writeValueAsString(requestDto);
String response = send2Mark.send(SPO_CREATE_PATH, requestJson);
JsonObject jsonObject = JsonParser.parseString(response).getAsJsonObject();
if (jsonObject.get("success").getAsBoolean()) {
log.info("TDI successfully sent. Mark ID = {}", jsonObject.get("id").getAsString());
} else {
log.warn("Failed to send TDI: {}", jsonObject);
}
} catch (Exception e) {
log.error("Error sending TDI: {}", e.getMessage(), e);
}
}
}
//------------------------
@Slf4j
@Service
@RequiredArgsConstructor
public class TransferMonitoringService {
private final List observers;
private final TransferRepository transferRepository;
private final ClientRepository clientRepository;
private final MarkService markService;
private final TdiBuilderService tdiBuilderService;
public void monitorNewTransfers() {
List whereParts = new ArrayList();
List allParams = new ArrayList();
for (TransferObserver observer : observers) {
String condition = observer.getSqlCondition();
if (condition != null) {
whereParts.add("(" + condition + ")");
allParams.addAll(observer.getSqlParameters());
}
}
if (whereParts.isEmpty()) {
log.warn("There are no conditions for monitoring transfers (the observers returned it empty).");
return;
}
String whereClause = String.join(" OR ", whereParts);
List newTransfers = transferRepository.findNewTransfers(whereClause, allParams);
for (TransferDto transfer : newTransfers) {
ClientDto client = clientRepository.findById(transfer.getClientId());
TdiRequestDto request = tdiBuilderService.build(transfer, client);
markService.sendTdi(request);
}
}
}
//---------------------------------
@Slf4j
@Component
@RequiredArgsConstructor
public class TransferScheduler {
private final TransferMonitoringService transferMonitoringService;
@Value("${monitoring.transfer.enabled}")
private boolean monitoringEnabled;
@Scheduled(cron = "${monitoring.transfer.cron}") // default every 5 minut
public void monitorTransfers() {
log.info("Start monitoring new transfers...");
if (!monitoringEnabled) {
log.info("Transfer monitoring is disabled via the configuration.");
return;
}
transferMonitoringService.monitorNewTransfers();
}
}