Skip to content

Commit 28ac767

Browse files
feat: completed bank app
1 parent 546b685 commit 28ac767

File tree

27 files changed

+1430
-1
lines changed

27 files changed

+1430
-1
lines changed

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

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

33
import com.codedifferently.lesson17.bank.exceptions.AccountNotFoundException;
4+
import com.codedifferently.lesson17.bank.exceptions.IllegalOperationException;
45
import java.util.HashMap;
56
import java.util.Map;
67
import java.util.Set;
78
import java.util.UUID;
89

9-
/** Represents a bank ATM. */
10+
/** Represents a bank ATM with audit logging capabilities. */
1011
public class BankAtm {
1112

1213
private final Map<UUID, Customer> customerById = new HashMap<>();
1314
private final Map<String, CheckingAccount> accountByNumber = new HashMap<>();
15+
private final AuditLog auditLog = new AuditLog();
16+
17+
/**
18+
* Gets the audit log for this ATM.
19+
*
20+
* @return The audit log instance.
21+
*/
22+
public AuditLog getAuditLog() {
23+
return auditLog;
24+
}
1425

1526
/**
1627
* Adds a checking account to the bank.
@@ -25,6 +36,11 @@ public void addAccount(CheckingAccount account) {
2536
owner -> {
2637
customerById.put(owner.getId(), owner);
2738
});
39+
40+
// Log account creation
41+
String accountType = account instanceof SavingsAccount ? "SavingsAccount" : "CheckingAccount";
42+
auditLog.logTransaction(account.getAccountNumber(), "ACCOUNT_CREATED",
43+
account.getBalance(), "New " + accountType + " created");
2844
}
2945

3046
/**
@@ -48,17 +64,31 @@ public Set<CheckingAccount> findAccountsByCustomerId(UUID customerId) {
4864
public void depositFunds(String accountNumber, double amount) {
4965
CheckingAccount account = getAccountOrThrow(accountNumber);
5066
account.deposit(amount);
67+
68+
// Log the transaction
69+
auditLog.logTransaction(accountNumber, "DEPOSIT", amount, "Cash deposit");
5170
}
5271

5372
/**
5473
* Deposits funds into an account using a check.
74+
* Note: SavingsAccount does not support check deposits.
5575
*
5676
* @param accountNumber The account number.
5777
* @param check The check to deposit.
5878
*/
5979
public void depositFunds(String accountNumber, Check check) {
6080
CheckingAccount account = getAccountOrThrow(accountNumber);
81+
82+
// Restrict check operations for SavingsAccount (SOLID: Liskov Substitution Principle)
83+
if (account instanceof SavingsAccount) {
84+
throw new IllegalOperationException("SavingsAccount does not support check operations");
85+
}
86+
6187
check.depositFunds(account);
88+
89+
// Log the transaction
90+
auditLog.logTransaction(accountNumber, "CHECK_DEPOSIT", check.getAmount(),
91+
"Check deposit from account " + check.getAccount().getAccountNumber());
6292
}
6393

6494
/**

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 amount of the check.
40+
*
41+
* @return The check amount.
42+
*/
43+
public double getAmount() {
44+
return amount;
45+
}
46+
47+
/**
48+
* Gets the account the check is drawn on.
49+
*
50+
* @return The source account.
51+
*/
52+
public CheckingAccount getAccount() {
53+
return account;
54+
}
55+
56+
/**
57+
* Gets the check number.
58+
*
59+
* @return The check number.
60+
*/
61+
public String getCheckNumber() {
62+
return checkNumber;
63+
}
64+
3865
/** Voids the check. */
3966
public void voidCheck() {
4067
isVoided = true;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.codedifferently.lesson17.bank.exceptions;
2+
3+
/**
4+
* Exception thrown when an illegal operation is attempted on an account.
5+
* For example, trying to write checks against a SavingsAccount.
6+
*/
7+
public class IllegalOperationException extends RuntimeException {
8+
9+
/**
10+
* Creates a new IllegalOperationException with the specified message.
11+
*
12+
* @param message The error message.
13+
*/
14+
public IllegalOperationException(String message) {
15+
super(message);
16+
}
17+
18+
/**
19+
* Creates a new IllegalOperationException with the specified message and cause.
20+
*
21+
* @param message The error message.
22+
* @param cause The underlying cause.
23+
*/
24+
public IllegalOperationException(String message, Throwable cause) {
25+
super(message, cause);
26+
}
27+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#
2+
# https://help.github.com/articles/dealing-with-line-endings/
3+
#
4+
# Linux start script should use lf
5+
/gradlew text eol=lf
6+
7+
# These are Windows script files and should use crlf
8+
*.bat text eol=crlf
9+

lesson_17/jaizelc/bank/.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Ignore Gradle project-specific cache directory
2+
.gradle
3+
4+
# Ignore Gradle build output directory
5+
build
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
plugins {
2+
// Apply the application plugin to add support for building a CLI application in Java.
3+
application
4+
eclipse
5+
jacoco
6+
id("io.freefair.lombok") version "8.10.2"
7+
id("com.diffplug.spotless") version "6.25.0"
8+
id("org.springframework.boot") version "3.4.0"
9+
id("com.adarshr.test-logger") version "4.0.0"
10+
}
11+
12+
apply(plugin = "io.spring.dependency-management")
13+
14+
repositories {
15+
// Use Maven Central for resolving dependencies.
16+
mavenCentral()
17+
}
18+
19+
dependencies {
20+
// Use JUnit Jupiter for testing.
21+
testImplementation("org.junit.jupiter:junit-jupiter:5.11.3")
22+
testImplementation("org.springframework.boot:spring-boot-starter-test")
23+
testImplementation("org.assertj:assertj-core:3.26.3")
24+
testImplementation("at.favre.lib:bcrypt:0.10.2")
25+
26+
// This dependency is used by the application.
27+
implementation("com.google.guava:guava:33.3.1-jre")
28+
implementation("com.google.code.gson:gson:2.11.0")
29+
implementation("org.projectlombok:lombok:1.18.30")
30+
implementation("org.springframework.boot:spring-boot-starter")
31+
}
32+
33+
application {
34+
// Define the main class for the application.
35+
mainClass.set("com.codedifferently.lesson17.Lesson17")
36+
}
37+
38+
tasks.named<Test>("test") {
39+
// Use JUnit Platform for unit tests.
40+
useJUnitPlatform()
41+
finalizedBy(tasks.jacocoTestReport)
42+
}
43+
44+
tasks.jacocoTestReport {
45+
dependsOn(tasks.test)
46+
reports {
47+
xml.required = true
48+
}
49+
}
50+
51+
tasks.jacocoTestCoverageVerification {
52+
violationRules {
53+
rule {
54+
limit {
55+
minimum = "0.8".toBigDecimal()
56+
}
57+
}
58+
}
59+
}
60+
61+
tasks.check {
62+
dependsOn(tasks.jacocoTestCoverageVerification)
63+
}
64+
65+
configure<com.diffplug.gradle.spotless.SpotlessExtension> {
66+
67+
format("misc", {
68+
// define the files to apply `misc` to
69+
target("*.gradle", ".gitattributes", ".gitignore")
70+
71+
// define the steps to apply to those files
72+
trimTrailingWhitespace()
73+
indentWithTabs() // or spaces. Takes an integer argument if you don't like 4
74+
endWithNewline()
75+
})
76+
77+
java {
78+
// don't need to set target, it is inferred from java
79+
80+
// apply a specific flavor of google-java-format
81+
googleJavaFormat()
82+
// fix formatting of type annotations
83+
formatAnnotations()
84+
}
85+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.codedifferently.lesson17;
2+
3+
import org.springframework.boot.SpringApplication;
4+
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
import org.springframework.context.annotation.Configuration;
6+
7+
@Configuration
8+
@SpringBootApplication(scanBasePackages = "com.codedifferently")
9+
public class Lesson17 {
10+
11+
public static void main(String[] args) {
12+
var application = new SpringApplication(Lesson17.class);
13+
application.run(args);
14+
}
15+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package com.codedifferently.lesson17.bank;
2+
3+
import java.time.LocalDateTime;
4+
import java.util.ArrayList;
5+
import java.util.List;
6+
7+
/**
8+
* Represents an audit log entry for banking transactions. Follows Single Responsibility Principle -
9+
* only handles transaction logging.
10+
*/
11+
public class AuditLog {
12+
13+
/** Represents a single audit entry. */
14+
public static class AuditEntry {
15+
private final LocalDateTime timestamp;
16+
private final String accountNumber;
17+
private final String transactionType;
18+
private final double amount;
19+
private final String description;
20+
21+
/**
22+
* Creates a new audit entry.
23+
*
24+
* @param accountNumber The account number involved in the transaction.
25+
* @param transactionType The type of transaction (DEPOSIT, WITHDRAWAL, etc.).
26+
* @param amount The transaction amount.
27+
* @param description Additional details about the transaction.
28+
*/
29+
public AuditEntry(
30+
String accountNumber, String transactionType, double amount, String description) {
31+
this.timestamp = LocalDateTime.now();
32+
this.accountNumber = accountNumber;
33+
this.transactionType = transactionType;
34+
this.amount = amount;
35+
this.description = description;
36+
}
37+
38+
public LocalDateTime getTimestamp() {
39+
return timestamp;
40+
}
41+
42+
public String getAccountNumber() {
43+
return accountNumber;
44+
}
45+
46+
public String getTransactionType() {
47+
return transactionType;
48+
}
49+
50+
public double getAmount() {
51+
return amount;
52+
}
53+
54+
public String getDescription() {
55+
return description;
56+
}
57+
58+
@Override
59+
public String toString() {
60+
return String.format(
61+
"[%s] %s - Account: %s, Amount: $%.2f - %s",
62+
timestamp, transactionType, accountNumber, amount, description);
63+
}
64+
}
65+
66+
private final List<AuditEntry> entries = new ArrayList<>();
67+
68+
/**
69+
* Logs a transaction.
70+
*
71+
* @param accountNumber The account number.
72+
* @param transactionType The transaction type.
73+
* @param amount The amount.
74+
* @param description The description.
75+
*/
76+
public void logTransaction(
77+
String accountNumber, String transactionType, double amount, String description) {
78+
entries.add(new AuditEntry(accountNumber, transactionType, amount, description));
79+
}
80+
81+
/**
82+
* Gets all audit entries.
83+
*
84+
* @return List of all audit entries.
85+
*/
86+
public List<AuditEntry> getAuditEntries() {
87+
return new ArrayList<>(entries); // Return copy for immutability
88+
}
89+
90+
/**
91+
* Gets audit entries for a specific account.
92+
*
93+
* @param accountNumber The account number to filter by.
94+
* @return List of audit entries for the account.
95+
*/
96+
public List<AuditEntry> getAuditEntriesForAccount(String accountNumber) {
97+
return entries.stream()
98+
.filter(entry -> entry.getAccountNumber().equals(accountNumber))
99+
.toList();
100+
}
101+
}

0 commit comments

Comments
 (0)