Skip to content

Commit 4b8a21f

Browse files
committed
Add permanent banning and prevent banned users from bidding
1 parent a16edb4 commit 4b8a21f

5 files changed

Lines changed: 116 additions & 32 deletions

File tree

client/src/main/java/com/auction/controller/AuctionListController.java

Lines changed: 83 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
import java.time.LocalDateTime;
1818
import java.time.format.DateTimeFormatter;
1919
import java.time.format.DateTimeParseException;
20+
import java.util.HashMap;
2021
import java.util.List;
22+
import java.util.Map;
2123
import java.util.function.Consumer;
2224
import javafx.application.Platform;
2325
import javafx.beans.property.SimpleObjectProperty;
@@ -455,35 +457,90 @@ private void onBanUser() {
455457
showStatus("Admin accounts cannot be banned.", true);
456458
return;
457459
}
458-
TextInputDialog dlg =
459-
new TextInputDialog(LocalDateTime.now().plusDays(7).format(BAN_EXPIRY_FMT));
460-
dlg.setTitle("Ban User");
461-
dlg.setHeaderText("Enter automatic unban date and time (DD/MM/YYYY HH:MM):");
462-
dlg.showAndWait()
463-
.ifPresent(
464-
expiryText -> {
465-
try {
466-
LocalDateTime expiry = LocalDateTime.parse(expiryText.trim(), BAN_EXPIRY_FMT);
467-
LocalDateTime now = LocalDateTime.now();
468-
if (!expiry.isAfter(now)) {
469-
showStatus(
470-
"Unban time must be later than the current date and time.", true);
471-
return;
472-
}
473-
long durationSeconds =
474-
Math.max(1, java.time.Duration.between(now, expiry).getSeconds());
475-
appState.restUserService.banUser(user.getId(), durationSeconds);
476-
refreshUsers();
477-
showStatus("User banned until " + expiry.format(BAN_EXPIRY_FMT) + ".", false);
478-
} catch (DateTimeParseException e) {
479-
showStatus("Use DD/MM/YYYY HH:MM, e.g. 25/12/2026 14:30.", true);
480-
} catch (Exception e) {
481-
showStatus(e.getMessage(), true);
482-
}
483-
});
460+
showBanDialog(user);
484461
});
485462
}
486463

464+
private void showBanDialog(User user) {
465+
Dialog<Map<String, Object>> dlg = new Dialog<>();
466+
dlg.setTitle("Ban User");
467+
dlg.setHeaderText("Ban user \"" + user.getUsername() + "\"");
468+
469+
GridPane grid = new GridPane();
470+
grid.setHgap(10);
471+
grid.setVgap(10);
472+
grid.setPadding(new Insets(20, 20, 10, 20));
473+
474+
javafx.scene.control.CheckBox permanentCheckBox =
475+
new javafx.scene.control.CheckBox("Permanent ban");
476+
javafx.scene.control.TextField dateField =
477+
new javafx.scene.control.TextField(LocalDateTime.now().plusDays(7).format(BAN_EXPIRY_FMT));
478+
dateField.setPromptText("DD/MM/YYYY HH:MM");
479+
Label dateLabel = new Label("Unban date and time:");
480+
481+
grid.add(permanentCheckBox, 0, 0, 2, 1);
482+
grid.add(dateLabel, 0, 1);
483+
grid.add(dateField, 1, 1);
484+
485+
permanentCheckBox
486+
.selectedProperty()
487+
.addListener(
488+
(obs, oldVal, newVal) -> {
489+
dateLabel.setDisable(newVal);
490+
dateField.setDisable(newVal);
491+
if (newVal) {
492+
dateLabel.setStyle("-fx-opacity: 0.4;");
493+
dateField.setStyle("-fx-opacity: 0.4;");
494+
} else {
495+
dateLabel.setStyle("");
496+
dateField.setStyle("");
497+
}
498+
});
499+
500+
dlg.getDialogPane().setContent(grid);
501+
ButtonType banBtn = new ButtonType("Ban", ButtonBar.ButtonData.OK_DONE);
502+
dlg.getDialogPane().getButtonTypes().addAll(banBtn, ButtonType.CANCEL);
503+
504+
dlg.setResultConverter(
505+
btn -> {
506+
if (btn != banBtn) return null;
507+
Map<String, Object> result = new HashMap<>();
508+
result.put("permanent", permanentCheckBox.isSelected());
509+
result.put("dateText", dateField.getText());
510+
return result;
511+
});
512+
513+
dlg.showAndWait()
514+
.ifPresent(
515+
result -> {
516+
try {
517+
boolean permanent = (boolean) result.get("permanent");
518+
if (permanent) {
519+
appState.restUserService.banUser(user.getId(), 0, true);
520+
refreshUsers();
521+
showStatus("User banned permanently.", false);
522+
} else {
523+
String dateText = (String) result.get("dateText");
524+
LocalDateTime expiry = LocalDateTime.parse(dateText.trim(), BAN_EXPIRY_FMT);
525+
LocalDateTime now = LocalDateTime.now();
526+
if (!expiry.isAfter(now)) {
527+
showStatus("Unban time must be later than the current date and time.", true);
528+
return;
529+
}
530+
long durationSeconds =
531+
Math.max(1, java.time.Duration.between(now, expiry).getSeconds());
532+
appState.restUserService.banUser(user.getId(), durationSeconds, false);
533+
refreshUsers();
534+
showStatus("User banned until " + expiry.format(BAN_EXPIRY_FMT) + ".", false);
535+
}
536+
} catch (DateTimeParseException e) {
537+
showStatus("Use DD/MM/YYYY HH:MM, e.g. 25/12/2026 14:30.", true);
538+
} catch (Exception e) {
539+
showStatus(e.getMessage(), true);
540+
}
541+
});
542+
}
543+
487544
@FXML
488545
private void onUnbanUser() {
489546
withSelectedUser(

client/src/main/java/com/auction/service/rest/RestUserService.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,16 @@ public List<User> findAllUsers() {
119119
}
120120

121121
public User banUser(String userId, long durationSeconds) {
122+
return banUser(userId, durationSeconds, false);
123+
}
124+
125+
public User banUser(String userId, long durationSeconds, boolean permanent) {
122126
try {
123-
String response =
124-
http.post("/users/" + userId + "/ban?durationSeconds=" + durationSeconds, "{}");
127+
String url = "/users/" + userId + "/ban?permanent=" + permanent;
128+
if (!permanent) {
129+
url += "&durationSeconds=" + durationSeconds;
130+
}
131+
String response = http.post(url, "{}");
125132
Map<String, Object> map = http.getGson().fromJson(response, MAP);
126133
return JsonMappers.toUser(map);
127134
} catch (IOException e) {

server/src/main/java/com/auction/server/controller/UserController.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,11 @@ public void handleDeductBalance(Context ctx) {
160160

161161
public void handleBanUser(Context ctx) {
162162
String id = ctx.pathParam("id");
163-
long durationSeconds = Long.parseLong(ctx.queryParam("durationSeconds"));
164-
User user = userService.banUser(id, durationSeconds, getAuthenticatedUser(ctx));
163+
String durationParam = ctx.queryParam("durationSeconds");
164+
String permanentParam = ctx.queryParam("permanent");
165+
boolean permanent = "true".equalsIgnoreCase(permanentParam);
166+
long durationSeconds = permanent ? 0 : Long.parseLong(durationParam);
167+
User user = userService.banUser(id, durationSeconds, permanent, getAuthenticatedUser(ctx));
165168
if (banExpiryScheduler != null) banExpiryScheduler.scheduleIfTemporary(user);
166169
List<String> revertedItemIds = revertWinningBidsForUnavailableUser(id);
167170
broadcastUserBanned(id);

shared/src/main/java/com/auction/service/BidService.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import com.auction.exception.UserNotFoundException;
1010
import com.auction.model.AuctionStatus;
1111
import com.auction.model.AutoBid;
12+
import com.auction.model.BannableUser;
1213
import com.auction.model.Bid;
1314
import com.auction.model.Bidder;
1415
import com.auction.model.Item;
@@ -119,6 +120,9 @@ public Bid placeBid(User user, String itemId, double amount) {
119120
if (!(user instanceof Bidder) && !(user instanceof Seller))
120121
throw new UnauthorizedActionException("Only bidders and sellers can place bids.");
121122

123+
if (user instanceof BannableUser bu && bu.isBanned())
124+
throw new UnauthorizedActionException("Banned users cannot place bids.");
125+
122126
Bid bid;
123127
ReentrantLock userLock = UserLockManager.getLock(user.getId());
124128
ReentrantLock itemLock = ItemLockManager.getLock(itemId);
@@ -207,6 +211,9 @@ public AutoBid setAutoBid(User user, String itemId, double maxBid, double increm
207211
if (!(user instanceof Bidder) && !(user instanceof Seller))
208212
throw new UnauthorizedActionException("Only bidders and sellers can use auto-bidding.");
209213

214+
if (user instanceof BannableUser bu && bu.isBanned())
215+
throw new UnauthorizedActionException("Banned users cannot use auto-bidding.");
216+
210217
AutoBid autoBid;
211218
ReentrantLock userLock = UserLockManager.getLock(user.getId());
212219
ReentrantLock itemLock = ItemLockManager.getLock(itemId);

shared/src/main/java/com/auction/service/UserService.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,14 @@ public Optional<User> findById(String bidderId) {
6767
}
6868

6969
public User banUser(String targetUserId, long durationSeconds, User adminUser) {
70+
return banUser(targetUserId, durationSeconds, false, adminUser);
71+
}
72+
73+
public User banUser(
74+
String targetUserId, long durationSeconds, boolean permanent, User adminUser) {
7075
requireAdmin(adminUser);
71-
if (durationSeconds <= 0) throw new InvalidInputException("Ban duration must be positive.");
76+
if (!permanent && durationSeconds <= 0)
77+
throw new InvalidInputException("Ban duration must be positive.");
7278
User user =
7379
userRepository
7480
.findById(targetUserId)
@@ -77,7 +83,11 @@ public User banUser(String targetUserId, long durationSeconds, User adminUser) {
7783
throw new UnauthorizedActionException("Admin accounts cannot be banned.");
7884
if (!(user instanceof BannableUser bu))
7985
throw new IllegalStateException("This user type cannot be banned.");
80-
bu.banTemporary(durationSeconds);
86+
if (permanent) {
87+
bu.banPermanent();
88+
} else {
89+
bu.banTemporary(durationSeconds);
90+
}
8191
userRepository.save(user);
8292
return user;
8393
}

0 commit comments

Comments
 (0)