-
Notifications
You must be signed in to change notification settings - Fork 3.4k
/
Copy pathSplitwiseService.java
117 lines (97 loc) · 3.84 KB
/
SplitwiseService.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package splitwise;
import splitwise.splittype.EqualSplit;
import splitwise.splittype.PercentSplit;
import splitwise.splittype.Split;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
public class SplitwiseService {
private static SplitwiseService instance;
private final Map<String, User> users;
private final Map<String, Group> groups;
private static final String TRANSACTION_ID_PREFIX = "TXN";
private static final AtomicInteger transactionCounter = new AtomicInteger(0);
private SplitwiseService() {
users = new ConcurrentHashMap<>();
groups = new ConcurrentHashMap<>();
}
public static synchronized SplitwiseService getInstance() {
if (instance == null) {
instance = new SplitwiseService();
}
return instance;
}
public void addUser(User user) {
users.put(user.getId(), user);
}
public void addGroup(Group group) {
groups.put(group.getId(), group);
}
public void addExpense(String groupId, Expense expense) {
Group group = groups.get(groupId);
if (group != null) {
group.addExpense(expense);
splitExpense(expense);
updateBalances(expense);
}
}
private void splitExpense(Expense expense) {
double totalAmount = expense.getAmount();
List<Split> splits = expense.getSplits();
int totalSplits = splits.size();
double splitAmount = totalAmount / totalSplits;
for (Split split : splits) {
if (split instanceof EqualSplit) {
split.setAmount(splitAmount);
} else if (split instanceof PercentSplit percentSplit) {
split.setAmount(totalAmount * percentSplit.getPercent() / 100.0);
}
}
}
private void updateBalances(Expense expense) {
for (Split split : expense.getSplits()) {
User paidBy = expense.getPaidBy();
User user = split.getUser();
double amount = split.getAmount();
if (!paidBy.equals(user)) {
updateBalance(paidBy, user, amount);
updateBalance(user, paidBy, -amount);
}
}
}
private void updateBalance(User user1, User user2, double amount) {
String key = getBalanceKey(user1, user2);
user1.getBalances().put(key, user1.getBalances().getOrDefault(key, 0.0) + amount);
}
private String getBalanceKey(User user1, User user2) {
return user1.getId() + ":" + user2.getId();
}
public void settleBalance(String userId1, String userId2) {
User user1 = users.get(userId1);
User user2 = users.get(userId2);
if (user1 != null && user2 != null) {
String key = getBalanceKey(user1, user2);
double balance = user1.getBalances().getOrDefault(key, 0.0);
if (balance > 0) {
createTransaction(user1, user2, balance);
user1.getBalances().put(key, 0.0);
user2.getBalances().put(getBalanceKey(user2, user1), 0.0);
} else if (balance < 0) {
createTransaction(user2, user1, Math.abs(balance));
user1.getBalances().put(key, 0.0);
user2.getBalances().put(getBalanceKey(user2, user1), 0.0);
}
}
}
private void createTransaction(User sender, User receiver, double amount) {
String transactionId = generateTransactionId();
Transaction transaction = new Transaction(transactionId, sender, receiver, amount);
// Process the transaction
// ...
}
private String generateTransactionId() {
int transactionNumber = transactionCounter.incrementAndGet();
return TRANSACTION_ID_PREFIX + String.format("%06d", transactionNumber);
}
}