Spaces:
Running
Running
Update app/bank.py
Browse files- app/bank.py +312 -54
app/bank.py
CHANGED
@@ -1,102 +1,360 @@
|
|
1 |
-
# bank.py
|
2 |
-
# functionen für die Community Bank
|
3 |
from flask import jsonify
|
4 |
-
import logging
|
5 |
-
import datetime
|
6 |
-
import random
|
7 |
-
|
8 |
from datetime import datetime, timedelta
|
9 |
|
10 |
class BankAccount:
|
11 |
def __init__(self, user_id, balance=0.0, interest_rate=0.0):
|
12 |
-
|
|
|
|
|
|
|
|
|
13 |
self.user_id = user_id
|
14 |
-
self.balance = balance
|
15 |
-
self.interest_rate = interest_rate
|
16 |
self.last_interest_time = datetime.now()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
|
18 |
def deposit(self, amount):
|
19 |
-
"""Funktion zum Einzahlen auf das Konto."""
|
20 |
if amount <= 0:
|
21 |
raise ValueError("Einzahlungsbetrag muss positiv sein.")
|
22 |
self.balance += amount
|
|
|
23 |
return self.balance
|
24 |
|
25 |
def withdraw(self, amount):
|
26 |
-
"""Funktion zum Abheben vom Konto."""
|
27 |
if amount <= 0:
|
28 |
raise ValueError("Abhebungsbetrag muss positiv sein.")
|
29 |
if amount > self.balance:
|
30 |
raise ValueError("Nicht genügend Geld auf dem Konto.")
|
31 |
self.balance -= amount
|
|
|
32 |
return self.balance
|
33 |
|
34 |
def apply_interest(self):
|
35 |
-
"""Berechnet die Zinsen und fügt sie dem Konto hinzu (positive oder negative Zinsen)."""
|
36 |
current_time = datetime.now()
|
37 |
time_difference = current_time - self.last_interest_time
|
38 |
-
|
39 |
-
# Zinsen werden z.B. täglich angewendet (kann angepasst werden)
|
40 |
if time_difference >= timedelta(days=1):
|
41 |
days_passed = time_difference.days
|
42 |
-
|
43 |
-
|
44 |
-
|
|
|
|
|
|
|
45 |
self.last_interest_time = current_time
|
|
|
46 |
return self.balance
|
47 |
|
48 |
def get_balance(self):
|
49 |
-
"""Gibt den aktuellen Kontostand zurück (nach Zinsberechnung)."""
|
50 |
self.apply_interest()
|
51 |
return self.balance
|
52 |
|
|
|
|
|
53 |
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
# print(f"Neuer Kontostand nach Einzahlung: {player_account.get_balance()}")
|
62 |
|
63 |
-
|
64 |
-
|
65 |
-
# print(f"Neuer Kontostand nach Abhebung: {player_account.get_balance()}")
|
66 |
|
67 |
-
|
68 |
-
|
69 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
70 |
|
71 |
-
# b) /send Money to user
|
72 |
def send_money_command(from_user_id, to_user_id, amount):
|
73 |
if amount <= 0:
|
74 |
return jsonify({"type": 4, "data": {"content": "Ungültiger Betrag!"}})
|
75 |
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
85 |
|
86 |
-
|
87 |
-
|
88 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
89 |
def daily_reward(user_id):
|
90 |
"""Gives daily reward of 100 credits to the user"""
|
91 |
-
|
92 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
93 |
|
94 |
-
if
|
95 |
-
|
96 |
-
return jsonify({"type": 4, "data": {"content": "Du hast bereits deine tägliche Belohnung erhalten!"}})
|
97 |
|
98 |
-
|
99 |
-
|
100 |
-
update_user_data(user_id, {'credits': new_credits, 'last_claimed': datetime.datetime.now()})
|
101 |
|
102 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
from flask import jsonify
|
|
|
|
|
|
|
|
|
2 |
from datetime import datetime, timedelta
|
3 |
|
4 |
class BankAccount:
|
5 |
def __init__(self, user_id, balance=0.0, interest_rate=0.0):
|
6 |
+
if not isinstance(user_id, (int, str)):
|
7 |
+
raise ValueError("user_id muss eine Zahl oder String sein")
|
8 |
+
if balance < 0:
|
9 |
+
raise ValueError("Startguthaben kann nicht negativ sein")
|
10 |
+
|
11 |
self.user_id = user_id
|
12 |
+
self.balance = float(balance)
|
13 |
+
self.interest_rate = float(interest_rate)
|
14 |
self.last_interest_time = datetime.now()
|
15 |
+
self.transaction_history = []
|
16 |
+
|
17 |
+
def add_transaction(self, transaction_type, amount, description=""):
|
18 |
+
transaction = {
|
19 |
+
"timestamp": datetime.now(),
|
20 |
+
"type": transaction_type,
|
21 |
+
"amount": amount,
|
22 |
+
"balance_after": self.balance,
|
23 |
+
"description": description
|
24 |
+
}
|
25 |
+
self.transaction_history.append(transaction)
|
26 |
|
27 |
def deposit(self, amount):
|
|
|
28 |
if amount <= 0:
|
29 |
raise ValueError("Einzahlungsbetrag muss positiv sein.")
|
30 |
self.balance += amount
|
31 |
+
self.add_transaction("deposit", amount)
|
32 |
return self.balance
|
33 |
|
34 |
def withdraw(self, amount):
|
|
|
35 |
if amount <= 0:
|
36 |
raise ValueError("Abhebungsbetrag muss positiv sein.")
|
37 |
if amount > self.balance:
|
38 |
raise ValueError("Nicht genügend Geld auf dem Konto.")
|
39 |
self.balance -= amount
|
40 |
+
self.add_transaction("withdraw", amount)
|
41 |
return self.balance
|
42 |
|
43 |
def apply_interest(self):
|
|
|
44 |
current_time = datetime.now()
|
45 |
time_difference = current_time - self.last_interest_time
|
46 |
+
|
|
|
47 |
if time_difference >= timedelta(days=1):
|
48 |
days_passed = time_difference.days
|
49 |
+
old_balance = self.balance
|
50 |
+
self.balance *= (1 + self.interest_rate) ** days_passed
|
51 |
+
interest_earned = self.balance - old_balance
|
52 |
+
|
53 |
+
self.add_transaction("interest", interest_earned,
|
54 |
+
f"Zinsen für {days_passed} Tage")
|
55 |
self.last_interest_time = current_time
|
56 |
+
|
57 |
return self.balance
|
58 |
|
59 |
def get_balance(self):
|
|
|
60 |
self.apply_interest()
|
61 |
return self.balance
|
62 |
|
63 |
+
def get_transaction_history(self):
|
64 |
+
return self.transaction_history
|
65 |
|
66 |
+
def transfer(self, target_account, amount):
|
67 |
+
if not isinstance(target_account, BankAccount):
|
68 |
+
raise ValueError("Ungültiges Zielkonto")
|
69 |
+
self.withdraw(amount)
|
70 |
+
target_account.deposit(amount)
|
71 |
+
self.add_transaction("transfer_out", amount, f"Transfer an {target_account.user_id}")
|
72 |
+
target_account.add_transaction("transfer_in", amount, f"Transfer von {self.user_id}")
|
|
|
73 |
|
74 |
+
def format_balance(self):
|
75 |
+
return f"{self.balance:,.2f} Credits"
|
|
|
76 |
|
77 |
+
class BankSystem:
|
78 |
+
def __init__(self):
|
79 |
+
self.accounts = {}
|
80 |
+
|
81 |
+
def create_account(self, user_id, initial_balance=0.0, interest_rate=0.05):
|
82 |
+
if user_id in self.accounts:
|
83 |
+
raise ValueError("Konto existiert bereits")
|
84 |
+
account = BankAccount(user_id, initial_balance, interest_rate)
|
85 |
+
self.accounts[user_id] = account
|
86 |
+
return account
|
87 |
+
|
88 |
+
def get_account(self, user_id):
|
89 |
+
return self.accounts.get(user_id)
|
90 |
+
|
91 |
+
def transfer_between_accounts(self, from_user_id, to_user_id, amount):
|
92 |
+
from_account = self.get_account(from_user_id)
|
93 |
+
to_account = self.get_account(to_user_id)
|
94 |
+
if not from_account or not to_account:
|
95 |
+
raise ValueError("Ein oder beide Konten existieren nicht")
|
96 |
+
from_account.transfer(to_account, amount)
|
97 |
|
|
|
98 |
def send_money_command(from_user_id, to_user_id, amount):
|
99 |
if amount <= 0:
|
100 |
return jsonify({"type": 4, "data": {"content": "Ungültiger Betrag!"}})
|
101 |
|
102 |
+
try:
|
103 |
+
# Überprüfe, ob der Absender genug Credits hat
|
104 |
+
from_user_data = get_user_data(from_user_id)
|
105 |
+
if from_user_data['credits'] < amount:
|
106 |
+
return jsonify({"type": 4, "data": {"content": "Nicht genug Credits!"}})
|
107 |
+
|
108 |
+
# Transaktion durchführen
|
109 |
+
update_user_data(from_user_id, {'credits': from_user_data['credits'] - amount})
|
110 |
+
to_user_data = get_user_data(to_user_id)
|
111 |
+
update_user_data(to_user_id, {'credits': to_user_data['credits'] + amount})
|
112 |
+
|
113 |
+
# Transaktion in History dokumentieren
|
114 |
+
transaction_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
115 |
+
log_transaction(from_user_id, to_user_id, amount, transaction_time)
|
116 |
+
|
117 |
+
return jsonify({
|
118 |
+
"type": 4,
|
119 |
+
"data": {
|
120 |
+
"content": f"Erfolgreich {amount:,.2f} Credits an den Benutzer {to_user_id} gesendet!"
|
121 |
+
}
|
122 |
+
})
|
123 |
|
124 |
+
except Exception as e:
|
125 |
+
logging.error(f"Fehler bei der Überweisung: {str(e)}")
|
126 |
+
return jsonify({
|
127 |
+
"type": 4,
|
128 |
+
"data": {
|
129 |
+
"content": "Ein Fehler ist bei der Überweisung aufgetreten!"
|
130 |
+
}
|
131 |
+
})
|
132 |
+
|
133 |
def daily_reward(user_id):
|
134 |
"""Gives daily reward of 100 credits to the user"""
|
135 |
+
try:
|
136 |
+
user_data = get_user_data(user_id)
|
137 |
+
last_claimed = user_data.get('last_claimed', None)
|
138 |
+
|
139 |
+
if last_claimed:
|
140 |
+
last_claimed_date = datetime.fromisoformat(str(last_claimed))
|
141 |
+
if last_claimed_date.date() == datetime.now().date():
|
142 |
+
return jsonify({
|
143 |
+
"type": 4,
|
144 |
+
"data": {
|
145 |
+
"content": "Du hast bereits deine tägliche Belohnung erhalten!"
|
146 |
+
}
|
147 |
+
})
|
148 |
+
|
149 |
+
# Zufällige Belohnung zwischen 50 und 150 Credits
|
150 |
+
reward_amount = random.randint(50, 150)
|
151 |
+
new_credits = user_data.get('credits', 0) + reward_amount
|
152 |
+
|
153 |
+
# Update user data
|
154 |
+
update_user_data(user_id, {
|
155 |
+
'credits': new_credits,
|
156 |
+
'last_claimed': datetime.now()
|
157 |
+
})
|
158 |
+
|
159 |
+
return jsonify({
|
160 |
+
"type": 4,
|
161 |
+
"data": {
|
162 |
+
"content": f"Du hast deine tägliche Belohnung erhalten: {reward_amount} Credits!"
|
163 |
+
}
|
164 |
+
})
|
165 |
+
|
166 |
+
except Exception as e:
|
167 |
+
logging.error(f"Fehler bei der täglichen Belohnung: {str(e)}")
|
168 |
+
return jsonify({
|
169 |
+
"type": 4,
|
170 |
+
"data": {
|
171 |
+
"content": "Ein Fehler ist beim Abholen der täglichen Belohnung aufgetreten!"
|
172 |
+
}
|
173 |
+
})
|
174 |
+
|
175 |
+
# Hilfsfunktionen (müssen implementiert werden)
|
176 |
+
def get_user_data(user_id):
|
177 |
+
"""Implementiere diese Funktion, um Benutzerdaten aus der Datenbank zu holen"""
|
178 |
+
pass
|
179 |
+
|
180 |
+
def update_user_data(user_id, data):
|
181 |
+
"""Implementiere diese Funktion, um Benutzerdaten in der Datenbank zu aktualisieren"""
|
182 |
+
pass
|
183 |
+
def log_transaction(from_user_id, to_user_id, amount, timestamp):
|
184 |
+
"""Implementiere diese Funktion, um Transaktionen in einem Log festzuhalten"""
|
185 |
+
try:
|
186 |
+
transaction_log = {
|
187 |
+
"from_user": from_user_id,
|
188 |
+
"to_user": to_user_id,
|
189 |
+
"amount": amount,
|
190 |
+
"timestamp": timestamp,
|
191 |
+
"transaction_id": generate_transaction_id()
|
192 |
+
}
|
193 |
+
|
194 |
+
# Hier könnte die Speicherung in einer Datenbank erfolgen
|
195 |
+
save_transaction_to_db(transaction_log)
|
196 |
+
|
197 |
+
logging.info(f"Transaktion erfolgreich protokolliert: {transaction_log}")
|
198 |
+
return True
|
199 |
+
|
200 |
+
except Exception as e:
|
201 |
+
logging.error(f"Fehler beim Protokollieren der Transaktion: {str(e)}")
|
202 |
+
return False
|
203 |
+
|
204 |
+
def generate_transaction_id():
|
205 |
+
"""Generiert eine einzigartige Transaktions-ID"""
|
206 |
+
timestamp = datetime.now().strftime('%Y%m%d%H%M%S')
|
207 |
+
random_suffix = ''.join(random.choices('0123456789ABCDEF', k=6))
|
208 |
+
return f"TRX-{timestamp}-{random_suffix}"
|
209 |
+
|
210 |
+
def save_transaction_to_db(transaction_data):
|
211 |
+
"""Implementiere diese Funktion zur Speicherung in der Datenbank"""
|
212 |
+
pass
|
213 |
+
|
214 |
+
def get_user_transaction_history(user_id):
|
215 |
+
"""Gibt die Transaktionshistorie eines Benutzers zurück"""
|
216 |
+
try:
|
217 |
+
# Implementiere hier die Datenbankabfrage
|
218 |
+
transactions = [] # Hole Transaktionen aus der Datenbank
|
219 |
+
return jsonify({
|
220 |
+
"type": 4,
|
221 |
+
"data": {
|
222 |
+
"content": "Transaktionshistorie",
|
223 |
+
"transactions": transactions
|
224 |
+
}
|
225 |
+
})
|
226 |
+
except Exception as e:
|
227 |
+
logging.error(f"Fehler beim Abrufen der Transaktionshistorie: {str(e)}")
|
228 |
+
return jsonify({
|
229 |
+
"type": 4,
|
230 |
+
"data": {
|
231 |
+
"content": "Fehler beim Abrufen der Transaktionshistorie"
|
232 |
+
}
|
233 |
+
})
|
234 |
+
|
235 |
+
def check_account_status(user_id):
|
236 |
+
"""Überprüft den Status eines Bankkontos"""
|
237 |
+
try:
|
238 |
+
user_data = get_user_data(user_id)
|
239 |
+
account = BankAccount(user_id, user_data.get('credits', 0))
|
240 |
+
|
241 |
+
status_info = {
|
242 |
+
"balance": account.format_balance(),
|
243 |
+
"last_transaction": user_data.get('last_transaction'),
|
244 |
+
"account_age": calculate_account_age(user_data.get('created_at')),
|
245 |
+
"interest_rate": f"{account.interest_rate * 100}%"
|
246 |
+
}
|
247 |
+
|
248 |
+
return jsonify({
|
249 |
+
"type": 4,
|
250 |
+
"data": {
|
251 |
+
"content": "Kontostatus",
|
252 |
+
"status": status_info
|
253 |
+
}
|
254 |
+
})
|
255 |
+
except Exception as e:
|
256 |
+
logging.error(f"Fehler beim Abrufen des Kontostatus: {str(e)}")
|
257 |
+
return jsonify({
|
258 |
+
"type": 4,
|
259 |
+
"data": {
|
260 |
+
"content": "Fehler beim Abrufen des Kontostatus"
|
261 |
+
}
|
262 |
+
})
|
263 |
+
|
264 |
+
def calculate_account_age(created_at):
|
265 |
+
"""Berechnet das Alter eines Kontos"""
|
266 |
+
if not created_at:
|
267 |
+
return "Unbekannt"
|
268 |
+
|
269 |
+
created_date = datetime.fromisoformat(str(created_at))
|
270 |
+
age = datetime.now() - created_date
|
271 |
+
return f"{age.days} Tage"
|
272 |
+
|
273 |
+
# Error Handler
|
274 |
+
def handle_transaction_error(error_type, message):
|
275 |
+
"""Zentrale Fehlerbehandlung für Transaktionen"""
|
276 |
+
logging.error(f"Transaktionsfehler: {error_type} - {message}")
|
277 |
+
return jsonify({
|
278 |
+
"type": 4,
|
279 |
+
"data": {
|
280 |
+
"content": f"Ein Fehler ist aufgetreten: {message}",
|
281 |
+
"error_type": error_type,
|
282 |
+
"timestamp": datetime.now().isoformat()
|
283 |
+
}
|
284 |
+
})
|
285 |
+
|
286 |
+
class TransactionError(Exception):
|
287 |
+
"""Benutzerdefinierte Exception für Transaktionsfehler"""
|
288 |
+
def __init__(self, message, error_type="TRANSACTION_ERROR"):
|
289 |
+
self.message = message
|
290 |
+
self.error_type = error_type
|
291 |
+
super().__init__(self.message)
|
292 |
+
|
293 |
+
# Hilfreiche Utility-Funktionen
|
294 |
+
def validate_transaction(from_user_id, to_user_id, amount):
|
295 |
+
"""Überprüft, ob eine Transaktion gültig ist"""
|
296 |
+
if from_user_id == to_user_id:
|
297 |
+
raise TransactionError("Selbstüberweisung nicht möglich", "SELF_TRANSACTION_ERROR")
|
298 |
|
299 |
+
if amount <= 0:
|
300 |
+
raise TransactionError("Ungültiger Betrag", "INVALID_AMOUNT_ERROR")
|
|
|
301 |
|
302 |
+
if not isinstance(amount, (int, float)):
|
303 |
+
raise TransactionError("Betrag muss eine Zahl sein", "INVALID_TYPE_ERROR")
|
|
|
304 |
|
305 |
+
return True
|
306 |
+
|
307 |
+
def format_currency(amount):
|
308 |
+
"""Formatiert einen Geldbetrag"""
|
309 |
+
return f"{amount:,.2f} Credits"
|
310 |
+
|
311 |
+
def get_account_summary(user_id):
|
312 |
+
"""Erstellt eine Zusammenfassung des Kontos"""
|
313 |
+
try:
|
314 |
+
account = get_user_data(user_id)
|
315 |
+
if not account:
|
316 |
+
raise TransactionError("Konto nicht gefunden", "ACCOUNT_NOT_FOUND")
|
317 |
+
|
318 |
+
return {
|
319 |
+
"user_id": user_id,
|
320 |
+
"balance": format_currency(account.get('credits', 0)),
|
321 |
+
"last_transaction": account.get('last_transaction'),
|
322 |
+
"account_status": "aktiv" if account.get('is_active', True) else "inaktiv",
|
323 |
+
"created_at": account.get('created_at'),
|
324 |
+
"last_daily_claim": account.get('last_claimed')
|
325 |
+
}
|
326 |
+
except Exception as e:
|
327 |
+
logging.error(f"Fehler beim Erstellen der Kontozusammenfassung: {str(e)}")
|
328 |
+
raise
|
329 |
+
|
330 |
+
# Initialisierung des Logging-Systems
|
331 |
+
def setup_logging():
|
332 |
+
"""Konfiguriert das Logging-System"""
|
333 |
+
logging.basicConfig(
|
334 |
+
level=logging.INFO,
|
335 |
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
336 |
+
handlers=[
|
337 |
+
logging.FileHandler('bank_transactions.log'),
|
338 |
+
logging.StreamHandler()
|
339 |
+
]
|
340 |
+
)
|
341 |
+
|
342 |
+
# Hauptfunktion zum Starten des Bank-Systems
|
343 |
+
def initialize_bank_system():
|
344 |
+
"""Initialisiert das Bank-System"""
|
345 |
+
try:
|
346 |
+
setup_logging()
|
347 |
+
logging.info("Bank-System wird gestartet...")
|
348 |
+
bank_system = BankSystem()
|
349 |
+
logging.info("Bank-System erfolgreich initialisiert")
|
350 |
+
return bank_system
|
351 |
+
except Exception as e:
|
352 |
+
logging.error(f"Fehler beim Initialisieren des Bank-Systems: {str(e)}")
|
353 |
+
raise
|
354 |
+
|
355 |
+
if __name__ == "__main__":
|
356 |
+
try:
|
357 |
+
bank_system = initialize_bank_system()
|
358 |
+
logging.info("Bank-System läuft und ist bereit")
|
359 |
+
except Exception as e:
|
360 |
+
logging.critical(f"Kritischer Fehler beim Starten des Bank-Systems: {str(e)}")
|