Skip to content

Commit

Permalink
Merge pull request #45 from Group-5/feat/User
Browse files Browse the repository at this point in the history
Merge Feat/user into release/v1.0.0, completing the backend of the application
  • Loading branch information
lucych authored Mar 10, 2026
2 parents eeff1df + 6982dd9 commit 54cd266
Show file tree
Hide file tree
Showing 19 changed files with 1,443 additions and 319 deletions.
38 changes: 31 additions & 7 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>${javafx.version}</version>
<classifier>win</classifier>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-graphics</artifactId>
<version>${javafx.version}</version>
<classifier>win</classifier>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-crypto -->
<dependency>
Expand All @@ -45,13 +52,17 @@
<version>3.1.0</version>
<scope>compile</scope>
</dependency>

<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.3.5</version>
</dependency>
</dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>6.1.10</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.9</version>
</dependency>
</dependencies>

<build>
<plugins>
Expand Down Expand Up @@ -112,6 +123,19 @@
<goal>shade</goal>
</goals>
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
<exclude>META-INF/NOTICE</exclude>
<exclude>META-INF/LICENSE</exclude>
<exclude>META-INF.versions.9.module-info</exclude>
</excludes>
</filter>
</filters>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>edu.group5.app.App</mainClass>
Expand Down
9 changes: 1 addition & 8 deletions src/main/java/edu/group5/app/App.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
package edu.group5.app;

import java.sql.Wrapper;
import java.util.HashMap;

import edu.group5.app.control.OrgAPIWrapper;
import tools.jackson.core.type.TypeReference;
import tools.jackson.databind.ObjectMapper;

/**
* Hello world!
*/
public class App {
public static void main(String[] args) throws InterruptedException {
Wrapper wrap = new Wrapper();
System.out.println("Hello World!");
}
}
22 changes: 19 additions & 3 deletions src/main/java/edu/group5/app/model/DBRepository.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package edu.group5.app.model;


import java.util.HashMap;
import java.util.Map;

import java.util.List;
/**
* Abstract base class for repositories that store their data
* in a database-related structure.
Expand All @@ -14,12 +12,30 @@
* </p>
*/
public abstract class DBRepository<K, V> extends Repository<K, V> {
protected final Map<K, V> contentLock;
/**
* Constructs a DBRepository with the given content.
*
* @param content the HashMap used to store repository entities
*/
protected DBRepository(Map<K, V> content) {
super(content);
this.contentLock = new HashMap<>();
}

protected void updateContentLock() {
synchronized (contentLock) {
contentLock.clear();
contentLock.putAll(this.content);
}
}

public abstract boolean addContent(V value);

/**
* Exports the repository content as a list of Object arrays, where each array represents a row of data.
* This method is intended for converting the repository content into a format suitable for database storage or export.
* @return a List of Object arrays representing the repository content for database export
*/
public abstract List<Object[]> export();
}
2 changes: 1 addition & 1 deletion src/main/java/edu/group5/app/model/Repository.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public abstract class Repository<K, V> {
*
* @param content the underlying data structure used to store entities
*/
public Repository(Map<K, V> content) {
protected Repository(Map<K, V> content) {
this.content = content;
}

Expand Down
103 changes: 90 additions & 13 deletions src/main/java/edu/group5/app/model/donation/DonationRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.List;
import java.math.BigDecimal;
import java.sql.Timestamp;

/**
* Repository class for Donation.
Expand All @@ -19,17 +22,70 @@ public class DonationRepository extends DBRepository<Integer, Donation> {
private final HashMap<Integer, Donation> content;

/**
* Constructs DonationRepository using Hashmap,
* and extends the content from DBRepository.
* @param content the underlying map used to store donations,
* where the key represents the donation ID
* Constructs DonationRepository from a list of Object[] rows from the database.
* @param rows List of Object[] representing donations from the DB.
* Each row must have 6 elements:
* [donationId, userId, organizationId, amount, date, paymentMethod]
* @throws IllegalArgumentException if the input list is null or any row is invalid
*/
public DonationRepository(HashMap<Integer, Donation> content){
if (content == null) {
throw new IllegalArgumentException("Content cannot be null");
public DonationRepository(List<Object[]> rows) {
super(new HashMap<>());
if (rows == null) {
throw new IllegalArgumentException("The list of rows cannot be null");
}
super(content);
this.content = content;
this.content = new HashMap<>();
for (Object[] row : rows) {
if (row == null || row.length != 6 ) {
throw new IllegalArgumentException("Each row must contain exactly 6 elements");
}
int donationId = (int) row[0];
int customerId = (int) row[1];
int organizationId = (int) row[2];
BigDecimal amount = (BigDecimal) row[3];
Timestamp date = (Timestamp) row[4];
String paymentMethod = (String) row[5];

Donation donation = new Donation(donationId, customerId, organizationId, amount, date, paymentMethod);
this.content.put(donationId, donation);
}
super.updateContentLock();
}

@Override
public List<Object[]> export() {
return content.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.map(entry -> { Donation donation = entry.getValue();
return new Object[] {
donation.donationId(), donation.userId(),
donation.organizationId(), donation.amount(),
donation.date(), donation.paymentMethod()};})
.toList();
}

/**
* Retrieves a donation by its ID.
* @param donationId the ID of the donation to retrieve
* @return the Donation object with the specified ID, or null if not found
* @throws IllegalArgumentException if the donationId is not positive
*/
public Donation getDonationById(int donationId) {
if (donationId <= 0) {
throw new IllegalArgumentException("Donation ID must be positive");
}
return content.get(donationId);
}

/**
* Generates the next donation ID based on the current maximum ID in the repository.
* @return the next donation ID to be used for a new donation
*/
public int getNextDonationId() {
return content.keySet().stream().max(Integer::compareTo).orElse(0) + 1;
} /* TODO change this when data database is introduced */

public Map<Integer, Donation> getAllDonations() {
return new HashMap<>(content);
}

/**
Expand All @@ -44,14 +100,15 @@ public DonationRepository(HashMap<Integer, Donation> content){
* @return {@code true} if the donation was successfully added, and
* {@code false} if a donation with the same ID already exists
*/
public boolean addDonation(Donation donation) {
@Override
public boolean addContent(Donation donation) {
if (donation == null) {
throw new IllegalArgumentException("Donation cannot be null");
}
if (content.containsKey(donation.donationId())){
return false;
return false;
}
content.put(donation.donationId(), donation);
this.content.put(donation.donationId(), donation);
return true;
}

Expand Down Expand Up @@ -98,9 +155,9 @@ public HashMap<Integer, Donation> sortByAmount() {

/**
* Returns all donations associated with a specific organization.
*
* @param orgNumber the organization ID to filter by
* @return a map containing all donations that belong to the given organization
* @throws IllegalArgumentException if the orgNumber is not positive
*/
public HashMap<Integer, Donation> filterByOrganization(int orgNumber) {
if (orgNumber <= 0) {
Expand All @@ -116,4 +173,24 @@ public HashMap<Integer, Donation> filterByOrganization(int orgNumber) {
LinkedHashMap::new
));
}

/**
* Returns all donations made by a specific user.
* @param userId the user ID to filter by
* @return a map containing all donations that belong to the given user
* @throws IllegalArgumentException if the userId is not positive
*/
public HashMap<Integer, Donation> filterByUser(int userId) {
if (userId <= 0) {
throw new IllegalArgumentException("User ID must be positive");
}
return content.entrySet().stream()
.filter(entry -> entry.getValue().userId() == userId)
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(e1, e2) -> e1,
LinkedHashMap::new
));
}
}
63 changes: 63 additions & 0 deletions src/main/java/edu/group5/app/model/donation/DonationService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package edu.group5.app.model.donation;
import java.time.Instant;

import java.math.BigDecimal;
import java.sql.Timestamp;
import edu.group5.app.model.organization.Organization;
import edu.group5.app.model.organization.OrganizationRepository;
import edu.group5.app.model.user.Customer;

/**
* DonationService class provides functionality for handling donations in the system.
* It interacts with the DonationRepository to manage donation records
* and the OrganizationRepository to validate organization information.
* The donate method allows a customer to make a donation to a specified organization,
* ensuring that the customer, organization, and donation amount are valid before processing the donation.
*/
public class DonationService {

private final DonationRepository donationRepository;
private final OrganizationRepository organizationRepository;

/**
* Constructor for DonationService. Initializes the service with the required repositories.
* @param donationRepository the repository for managing donation records
* @param organizationRepository the repository for managing organization information
* @throws IllegalArgumentException if either repository is null
*/
public DonationService(DonationRepository donationRepository,
OrganizationRepository organizationRepository) {
if (donationRepository == null) {
throw new IllegalArgumentException("DonationRepository cannot be null");
}
if (organizationRepository == null) {
throw new IllegalArgumentException("OrganizationRepository cannot be null");
}
this.donationRepository = donationRepository;
this.organizationRepository = organizationRepository;
}

/**
* Processes a donation from a customer to a specified organization with a given amount.
* Validates the customer, organization number, and donation amount before creating a donation record.
* @param customer the customer making the donation
* @param orgNumber the organization number to which the donation is made
* @param amount the amount of the donation
* @return true if the donation is successfully processed, false otherwise
*/
public boolean donate(Customer customer, int orgNumber, BigDecimal amount, String paymentMethod) {
if (customer == null || amount == null
|| amount.compareTo(BigDecimal.ZERO) <= 0 || paymentMethod.isBlank()) {
return false;
}
Organization org = organizationRepository.findByOrgNumber(orgNumber);
if (org == null) {
return false;
}
Donation donation =
new Donation(donationRepository.getNextDonationId(),
customer.getUserId(), org.orgNumber(), amount, Timestamp.from(Instant.now()), paymentMethod);
donationRepository.addContent(donation);
return true;
}
}
Loading

0 comments on commit 54cd266

Please sign in to comment.