-
Notifications
You must be signed in to change notification settings - Fork 3.4k
/
Copy pathSplitwiseService.cs
136 lines (118 loc) · 4.26 KB
/
SplitwiseService.cs
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
namespace Splitwise
{
public class SplitwiseService
{
private static SplitwiseService instance;
private readonly ConcurrentDictionary<string, User> users;
private readonly ConcurrentDictionary<string, Group> groups;
// Replace AtomicInteger with a simple int
private static int transactionCounter = 0;
private static readonly string TRANSACTION_ID_PREFIX = "TXN";
private SplitwiseService()
{
users = new ConcurrentDictionary<string, User>();
groups = new ConcurrentDictionary<string, Group>();
}
public static SplitwiseService GetInstance()
{
if (instance == null)
{
instance = new SplitwiseService();
}
return instance;
}
public void AddUser(User user)
{
users.TryAdd(user.Id, user);
}
public void AddGroup(Group group)
{
groups.TryAdd(group.Id, group);
}
public void AddExpense(string groupId, Expense expense)
{
if (groups.TryGetValue(groupId, out var group))
{
group.AddExpense(expense);
SplitExpense(expense);
UpdateBalances(expense);
}
}
private void SplitExpense(Expense expense)
{
double totalAmount = expense.Amount;
var splits = expense.Splits;
int totalSplits = splits.Count;
double splitAmount = totalAmount / totalSplits;
foreach (var split in splits)
{
if (split is EqualSplit)
{
split.Amount = splitAmount;
}
else if (split is PercentSplit percentSplit)
{
split.Amount = totalAmount * percentSplit.Percent / 100.0;
}
}
}
private void UpdateBalances(Expense expense)
{
foreach (var split in expense.Splits)
{
var paidBy = expense.PaidBy;
var user = split.User;
double amount = split.Amount;
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.Balances.AddOrUpdate(key, amount, (k, v) => v + amount);
}
private string GetBalanceKey(User user1, User user2)
{
return user1.Id + ":" + user2.Id;
}
public void SettleBalance(string userId1, string userId2)
{
if (users.TryGetValue(userId1, out var user1) && users.TryGetValue(userId2, out var user2))
{
string key = GetBalanceKey(user1, user2);
double balance = user1.Balances.GetValueOrDefault(key, 0.0);
if (balance > 0)
{
CreateTransaction(user1, user2, balance);
user1.Balances[key] = 0.0;
user2.Balances[GetBalanceKey(user2, user1)] = 0.0;
}
else if (balance < 0)
{
CreateTransaction(user2, user1, -balance);
user1.Balances[key] = 0.0;
user2.Balances[GetBalanceKey(user2, user1)] = 0.0;
}
}
}
private void CreateTransaction(User sender, User receiver, double amount)
{
string transactionId = GenerateTransactionId();
var transaction = new Transaction(transactionId, sender, receiver, amount);
// Process transaction (e.g., store it or notify users)
}
// Replace AtomicInteger logic with Interlocked.Increment
private string GenerateTransactionId()
{
int transactionNumber = Interlocked.Increment(ref transactionCounter);
return TRANSACTION_ID_PREFIX + transactionNumber.ToString("D6");
}
}
}