Skip to content

Commit f26b446

Browse files
committed
feat: implement AuditLog system for transaction tracking
1 parent b33bd24 commit f26b446

File tree

7 files changed

+438
-6
lines changed

7 files changed

+438
-6
lines changed
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package com.codedifferently.lesson17.bank;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
import java.util.UUID;
6+
7+
/**
8+
* Handles logging of all banking transactions for audit purposes. This class follows the Single
9+
* Responsibility Principle - it only handles logging.
10+
*/
11+
public class AuditLog {
12+
13+
private final List<Transaction> transactions;
14+
15+
/** Creates a new AuditLog with an empty transaction history. */
16+
public AuditLog() {
17+
this.transactions = new ArrayList<>();
18+
}
19+
20+
/** Logs a cash deposit transaction. */
21+
public void logDeposit(String accountNumber, double amount) {
22+
String transactionId = generateTransactionId();
23+
String description = String.format("Cash deposit of $%.2f", amount);
24+
Transaction transaction =
25+
new Transaction(transactionId, accountNumber, TransactionType.DEPOSIT, amount, description);
26+
transactions.add(transaction);
27+
}
28+
29+
/** Logs a check deposit transaction. */
30+
public void logCheckDeposit(String accountNumber, double amount, String checkNumber) {
31+
String transactionId = generateTransactionId();
32+
String description = String.format("Check deposit of $%.2f (Check #%s)", amount, checkNumber);
33+
Transaction transaction =
34+
new Transaction(transactionId, accountNumber, TransactionType.DEPOSIT, amount, description);
35+
transactions.add(transaction);
36+
}
37+
38+
/** Logs a withdrawal transaction. */
39+
public void logWithdrawal(String accountNumber, double amount) {
40+
String transactionId = generateTransactionId();
41+
String description = String.format("Cash withdrawal of $%.2f", amount);
42+
Transaction transaction =
43+
new Transaction(
44+
transactionId, accountNumber, TransactionType.WITHDRAWAL, amount, description);
45+
transactions.add(transaction);
46+
}
47+
48+
/** Logs a transfer transaction (money moving from one account to another via check). */
49+
public void logTransfer(String fromAccount, String toAccount, double amount, String checkNumber) {
50+
String transactionId = generateTransactionId();
51+
String description =
52+
String.format(
53+
"Transfer of $%.2f from %s to %s (Check #%s)",
54+
amount, fromAccount, toAccount, checkNumber);
55+
Transaction transaction =
56+
new Transaction(transactionId, toAccount, TransactionType.TRANSFER, amount, description);
57+
transactions.add(transaction);
58+
}
59+
60+
/** Gets all transactions for a specific account. */
61+
public List<Transaction> getTransactionsForAccount(String accountNumber) {
62+
return transactions.stream()
63+
.filter(transaction -> transaction.accountNumber().equals(accountNumber))
64+
.toList();
65+
}
66+
67+
/** Gets all transactions in the system. */
68+
public List<Transaction> getAllTransactions() {
69+
return new ArrayList<>(transactions);
70+
}
71+
72+
/** Gets the total number of transactions logged. */
73+
public int getTransactionCount() {
74+
return transactions.size();
75+
}
76+
77+
/** Generates a unique transaction ID. */
78+
private String generateTransactionId() {
79+
return "TXN-" + UUID.randomUUID().toString().substring(0, 8).toUpperCase();
80+
}
81+
}

lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/BankAtm.java

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,31 @@
11
package com.codedifferently.lesson17.bank;
22

3-
import com.codedifferently.lesson17.bank.exceptions.AccountNotFoundException;
43
import java.util.HashMap;
54
import java.util.Map;
65
import java.util.Set;
76
import java.util.UUID;
87

8+
import com.codedifferently.lesson17.bank.exceptions.AccountNotFoundException;
9+
910
/** Represents a bank ATM. */
1011
public class BankAtm {
1112

1213
private final Map<UUID, Customer> customerById = new HashMap<>();
1314
private final Map<String, Account> accountByNumber = new HashMap<>();
15+
private final AuditLog auditLog;
16+
17+
/** Creates a new BankAtm with a new audit log. */
18+
public BankAtm() {
19+
this.auditLog = new AuditLog();
20+
}
21+
22+
/**
23+
* Creates a new BankAtm with the specified audit log. This allows for dependency injection for
24+
* testing.
25+
*/
26+
public BankAtm(AuditLog auditLog) {
27+
this.auditLog = auditLog;
28+
}
1429

1530
/**
1631
* Adds an account to the bank.
@@ -66,6 +81,7 @@ public Set<Account> findAccountsByCustomerId(UUID customerId) {
6681
public void depositFunds(String accountNumber, double amount) {
6782
Account account = getAccountOrThrow(accountNumber);
6883
account.deposit(amount);
84+
auditLog.logDeposit(accountNumber, amount);
6985
}
7086

7187
/**
@@ -84,6 +100,13 @@ public void depositFunds(String accountNumber, Check check) {
84100

85101
CheckingAccount checkingAccount = (CheckingAccount) account;
86102
check.depositFunds(checkingAccount);
103+
104+
// Log the transfer from source to destination account
105+
auditLog.logTransfer(
106+
check.getSourceAccount().getAccountNumber(),
107+
accountNumber,
108+
check.getAmount(),
109+
check.getCheckNumber());
87110
}
88111

89112
/**
@@ -95,6 +118,16 @@ public void depositFunds(String accountNumber, Check check) {
95118
public void withdrawFunds(String accountNumber, double amount) {
96119
Account account = getAccountOrThrow(accountNumber);
97120
account.withdraw(amount);
121+
auditLog.logWithdrawal(accountNumber, amount);
122+
}
123+
124+
/**
125+
* Gets the audit log for transaction history.
126+
*
127+
* @return The audit log.
128+
*/
129+
public AuditLog getAuditLog() {
130+
return auditLog;
98131
}
99132

100133
/**

lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/Check.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,33 @@ public boolean getIsVoided() {
3535
return isVoided;
3636
}
3737

38+
/**
39+
* Gets the check number.
40+
*
41+
* @return The check number.
42+
*/
43+
public String getCheckNumber() {
44+
return checkNumber;
45+
}
46+
47+
/**
48+
* Gets the amount of the check.
49+
*
50+
* @return The check amount.
51+
*/
52+
public double getAmount() {
53+
return amount;
54+
}
55+
56+
/**
57+
* Gets the source account (the account the check is drawn on).
58+
*
59+
* @return The source account.
60+
*/
61+
public CheckingAccount getSourceAccount() {
62+
return account;
63+
}
64+
3865
/** Voids the check. */
3966
public void voidCheck() {
4067
isVoided = true;
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.codedifferently.lesson17.bank;
2+
3+
import java.time.LocalDateTime;
4+
5+
/**
6+
* Represents a single bank transaction for audit logging. This is an immutable record that captures
7+
* all the details of a transaction.
8+
*/
9+
public record Transaction(
10+
String transactionId,
11+
String accountNumber,
12+
TransactionType type,
13+
double amount,
14+
LocalDateTime timestamp,
15+
String description) {
16+
17+
/** Creates a new Transaction with the current timestamp. */
18+
public Transaction(
19+
String transactionId,
20+
String accountNumber,
21+
TransactionType type,
22+
double amount,
23+
String description) {
24+
this(transactionId, accountNumber, type, amount, LocalDateTime.now(), description);
25+
}
26+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.codedifferently.lesson17.bank;
2+
3+
/** Represents the different types of transactions that can occur in the banking system. */
4+
public enum TransactionType {
5+
/** Money being added to an account */
6+
DEPOSIT,
7+
8+
/** Money being removed from an account */
9+
WITHDRAWAL,
10+
11+
/** Money being moved from one account to another (via check) */
12+
TRANSFER,
13+
14+
/** Account being opened */
15+
ACCOUNT_OPENED,
16+
17+
/** Account being closed */
18+
ACCOUNT_CLOSED
19+
}
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
package com.codedifferently.lesson17.bank;
2+
3+
import java.util.List;
4+
5+
import static org.assertj.core.api.Assertions.assertThat;
6+
import static org.junit.jupiter.api.Assertions.assertEquals;
7+
import org.junit.jupiter.api.BeforeEach;
8+
import org.junit.jupiter.api.Test;
9+
10+
class AuditLogTest {
11+
12+
private AuditLog auditLog;
13+
14+
@BeforeEach
15+
void setUp() {
16+
auditLog = new AuditLog();
17+
}
18+
19+
@Test
20+
void testLogDeposit() {
21+
auditLog.logDeposit("123456789", 100.0);
22+
23+
List<Transaction> transactions = auditLog.getAllTransactions();
24+
assertEquals(1, transactions.size());
25+
26+
Transaction transaction = transactions.get(0);
27+
assertEquals("123456789", transaction.accountNumber());
28+
assertEquals(TransactionType.DEPOSIT, transaction.type());
29+
assertEquals(100.0, transaction.amount());
30+
assertThat(transaction.description()).contains("Cash deposit of $100.00");
31+
assertThat(transaction.transactionId()).startsWith("TXN-");
32+
}
33+
34+
@Test
35+
void testLogCheckDeposit() {
36+
auditLog.logCheckDeposit("987654321", 250.0, "CHK001");
37+
38+
List<Transaction> transactions = auditLog.getAllTransactions();
39+
assertEquals(1, transactions.size());
40+
41+
Transaction transaction = transactions.get(0);
42+
assertEquals("987654321", transaction.accountNumber());
43+
assertEquals(TransactionType.DEPOSIT, transaction.type());
44+
assertEquals(250.0, transaction.amount());
45+
assertThat(transaction.description()).contains("Check deposit of $250.00 (Check #CHK001)");
46+
}
47+
48+
@Test
49+
void testLogWithdrawal() {
50+
auditLog.logWithdrawal("555666777", 75.0);
51+
52+
List<Transaction> transactions = auditLog.getAllTransactions();
53+
assertEquals(1, transactions.size());
54+
55+
Transaction transaction = transactions.get(0);
56+
assertEquals("555666777", transaction.accountNumber());
57+
assertEquals(TransactionType.WITHDRAWAL, transaction.type());
58+
assertEquals(75.0, transaction.amount());
59+
assertThat(transaction.description()).contains("Cash withdrawal of $75.00");
60+
}
61+
62+
@Test
63+
void testLogTransfer() {
64+
auditLog.logTransfer("111111111", "222222222", 300.0, "CHK002");
65+
66+
List<Transaction> transactions = auditLog.getAllTransactions();
67+
assertEquals(1, transactions.size());
68+
69+
Transaction transaction = transactions.get(0);
70+
assertEquals("222222222", transaction.accountNumber()); // Logged to destination account
71+
assertEquals(TransactionType.TRANSFER, transaction.type());
72+
assertEquals(300.0, transaction.amount());
73+
assertThat(transaction.description())
74+
.contains("Transfer of $300.00 from 111111111 to 222222222 (Check #CHK002)");
75+
}
76+
77+
@Test
78+
void testGetTransactionsForAccount() {
79+
auditLog.logDeposit("123456789", 100.0);
80+
auditLog.logWithdrawal("123456789", 50.0);
81+
auditLog.logDeposit("987654321", 200.0);
82+
83+
List<Transaction> account1Transactions = auditLog.getTransactionsForAccount("123456789");
84+
List<Transaction> account2Transactions = auditLog.getTransactionsForAccount("987654321");
85+
86+
assertEquals(2, account1Transactions.size());
87+
assertEquals(1, account2Transactions.size());
88+
89+
// Verify account1 transactions
90+
assertThat(account1Transactions.get(0).type()).isEqualTo(TransactionType.DEPOSIT);
91+
assertThat(account1Transactions.get(1).type()).isEqualTo(TransactionType.WITHDRAWAL);
92+
93+
// Verify account2 transactions
94+
assertThat(account2Transactions.get(0).type()).isEqualTo(TransactionType.DEPOSIT);
95+
}
96+
97+
@Test
98+
void testGetTransactionCount() {
99+
assertEquals(0, auditLog.getTransactionCount());
100+
101+
auditLog.logDeposit("123456789", 100.0);
102+
assertEquals(1, auditLog.getTransactionCount());
103+
104+
auditLog.logWithdrawal("123456789", 50.0);
105+
assertEquals(2, auditLog.getTransactionCount());
106+
107+
auditLog.logTransfer("111111111", "222222222", 300.0, "CHK003");
108+
assertEquals(3, auditLog.getTransactionCount());
109+
}
110+
111+
@Test
112+
void testMultipleTransactions() {
113+
// Create a more complex scenario
114+
auditLog.logDeposit("ACCT001", 1000.0);
115+
auditLog.logCheckDeposit("ACCT002", 500.0, "CHK123");
116+
auditLog.logWithdrawal("ACCT001", 200.0);
117+
auditLog.logTransfer("ACCT001", "ACCT003", 150.0, "CHK456");
118+
119+
assertEquals(4, auditLog.getTransactionCount());
120+
121+
// Check ACCT001 has 2 transactions (deposit and withdrawal)
122+
List<Transaction> acct001Transactions = auditLog.getTransactionsForAccount("ACCT001");
123+
assertEquals(2, acct001Transactions.size());
124+
125+
// Check ACCT002 has 1 transaction (check deposit)
126+
List<Transaction> acct002Transactions = auditLog.getTransactionsForAccount("ACCT002");
127+
assertEquals(1, acct002Transactions.size());
128+
129+
// Check ACCT003 has 1 transaction (transfer destination)
130+
List<Transaction> acct003Transactions = auditLog.getTransactionsForAccount("ACCT003");
131+
assertEquals(1, acct003Transactions.size());
132+
assertEquals(TransactionType.TRANSFER, acct003Transactions.get(0).type());
133+
}
134+
}

0 commit comments

Comments
 (0)