Verschiebung nach anderem RePo - nun pro Projekt getrennt
This commit is contained in:
362
src/main/java/de/oaa/xxx/social/SocialController.java
Normal file
362
src/main/java/de/oaa/xxx/social/SocialController.java
Normal file
@@ -0,0 +1,362 @@
|
||||
package de.oaa.xxx.social;
|
||||
|
||||
import de.oaa.xxx.social.dto.ConversationSummary;
|
||||
import de.oaa.xxx.social.dto.FriendshipDto;
|
||||
import de.oaa.xxx.social.dto.MessageDto;
|
||||
import de.oaa.xxx.social.dto.UserProfile;
|
||||
import de.oaa.xxx.social.entity.FriendshipEntity;
|
||||
import de.oaa.xxx.social.entity.FriendshipEntity.Status;
|
||||
import de.oaa.xxx.social.entity.MessageCause;
|
||||
import de.oaa.xxx.social.entity.MessageEntity;
|
||||
import de.oaa.xxx.social.repository.FriendshipRepository;
|
||||
import de.oaa.xxx.social.repository.MessageRepository;
|
||||
import de.oaa.xxx.support.SupportUserService;
|
||||
import de.oaa.xxx.user.UserEntity;
|
||||
import de.oaa.xxx.user.UserRepository;
|
||||
import de.oaa.xxx.user.UserService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/social")
|
||||
public class SocialController {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(SocialController.class);
|
||||
|
||||
private final UserRepository userRepository;
|
||||
private final FriendshipRepository friendshipRepository;
|
||||
private final MessageRepository messageRepository;
|
||||
private final SseService sseService;
|
||||
private final SystemMessageService systemMessageService;
|
||||
private final UserService userService;
|
||||
|
||||
public SocialController(UserRepository userRepository,
|
||||
FriendshipRepository friendshipRepository,
|
||||
MessageRepository messageRepository,
|
||||
SseService sseService,
|
||||
SystemMessageService systemMessageService,
|
||||
UserService userService) {
|
||||
this.userRepository = userRepository;
|
||||
this.friendshipRepository = friendshipRepository;
|
||||
this.messageRepository = messageRepository;
|
||||
this.sseService = sseService;
|
||||
this.systemMessageService = systemMessageService;
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
record FriendRequestBody(UUID receiverId) {}
|
||||
record FriendshipActionBody(UUID friendshipId) {}
|
||||
record SendMessageBody(UUID receiverId, String text) {}
|
||||
|
||||
// ── User Profile ──
|
||||
|
||||
@GetMapping("/users/{userId}")
|
||||
public ResponseEntity<UserProfile> getUserProfile(@PathVariable UUID userId, Principal principal) {
|
||||
UUID myId = userService.requireUser(principal).getUserId();
|
||||
return userRepository.findById(userId)
|
||||
.map(u -> ResponseEntity.ok(toUserProfileWithStatus(u, myId)))
|
||||
.orElse(ResponseEntity.notFound().build());
|
||||
}
|
||||
|
||||
// ── User Search ──
|
||||
|
||||
@GetMapping("/users/search")
|
||||
public ResponseEntity<List<UserProfile>> searchUsers(@RequestParam String q, Principal principal) {
|
||||
UUID myId = userService.requireUser(principal).getUserId();
|
||||
|
||||
List<UserEntity> results = userRepository.findByNameContainingIgnoreCase(q);
|
||||
List<UserProfile> profiles = results.stream()
|
||||
.filter(u -> !u.getUserId().equals(myId))
|
||||
.limit(20)
|
||||
.map(u -> toUserProfileWithStatus(u, myId))
|
||||
.toList();
|
||||
return ResponseEntity.ok(profiles);
|
||||
}
|
||||
|
||||
// ── Friendship ──
|
||||
|
||||
@PostMapping("/friends/request")
|
||||
public ResponseEntity<Void> sendFriendRequest(@RequestBody FriendRequestBody body, Principal principal) {
|
||||
var me = userService.requireUser(principal);
|
||||
UUID myId = me.getUserId();
|
||||
|
||||
if (myId.equals(body.receiverId())) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
if (friendshipRepository.findExisting(myId, body.receiverId()).isPresent()) {
|
||||
return ResponseEntity.status(409).build();
|
||||
}
|
||||
FriendshipEntity f = new FriendshipEntity();
|
||||
f.setFriendshipId(UUID.randomUUID());
|
||||
f.setSenderId(myId);
|
||||
f.setReceiverId(body.receiverId());
|
||||
f.setStatus(Status.PENDING);
|
||||
f.setCreatedAt(LocalDateTime.now());
|
||||
friendshipRepository.save(f);
|
||||
LOGGER.info("User {} hat Freundschaftsanfrage an User {} gesendet", myId, body.receiverId());
|
||||
|
||||
String senderName = me.getName();
|
||||
systemMessageService.send(myId, body.receiverId(),
|
||||
senderName + " hat dir eine Freundschaftsanfrage gesendet.",
|
||||
"/community/benutzer.html?userId=" + myId,
|
||||
MessageCause.FRIENDREQUEST);
|
||||
|
||||
return ResponseEntity.status(201).build();
|
||||
}
|
||||
|
||||
@PostMapping("/friends/accept")
|
||||
public ResponseEntity<Void> acceptFriendRequest(@RequestBody FriendshipActionBody body, Principal principal) {
|
||||
UUID myId = userService.requireUser(principal).getUserId();
|
||||
|
||||
var fOpt = friendshipRepository.findById(body.friendshipId());
|
||||
if (fOpt.isEmpty()) return ResponseEntity.notFound().build();
|
||||
FriendshipEntity f = fOpt.get();
|
||||
if (!f.getReceiverId().equals(myId)) return ResponseEntity.status(403).build();
|
||||
|
||||
f.setStatus(Status.ACCEPTED);
|
||||
friendshipRepository.save(f);
|
||||
LOGGER.info("User {} hat Freundschaftsanfrage {} angenommen", myId, body.friendshipId());
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@DeleteMapping("/friends/reject")
|
||||
public ResponseEntity<Void> rejectOrRemoveFriend(@RequestBody FriendshipActionBody body, Principal principal) {
|
||||
UUID myId = userService.requireUser(principal).getUserId();
|
||||
|
||||
var fOpt = friendshipRepository.findById(body.friendshipId());
|
||||
if (fOpt.isEmpty()) return ResponseEntity.notFound().build();
|
||||
FriendshipEntity f = fOpt.get();
|
||||
if (!f.getSenderId().equals(myId) && !f.getReceiverId().equals(myId)) {
|
||||
return ResponseEntity.status(403).build();
|
||||
}
|
||||
friendshipRepository.delete(f);
|
||||
LOGGER.info("User {} hat Freundschaft/Anfrage {} gelöscht", myId, body.friendshipId());
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
@GetMapping("/friends/user/{userId}")
|
||||
public ResponseEntity<List<UserProfile>> getFriendsOfUser(@PathVariable UUID userId, Principal principal) {
|
||||
userService.requireUser(principal);
|
||||
List<UserProfile> profiles = friendshipRepository.findFriends(userId, Status.ACCEPTED).stream()
|
||||
.map(f -> {
|
||||
UUID friendId = f.getSenderId().equals(userId) ? f.getReceiverId() : f.getSenderId();
|
||||
return userRepository.findById(friendId)
|
||||
.map(u -> new UserProfile(u.getUserId(), u.getName(), u.getProfilePicture(), u.getProfilePictureHq(), null))
|
||||
.orElse(null);
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.toList();
|
||||
return ResponseEntity.ok(profiles);
|
||||
}
|
||||
|
||||
@GetMapping("/friends")
|
||||
public ResponseEntity<List<FriendshipDto>> getFriends(Principal principal) {
|
||||
UUID myId = userService.requireUser(principal).getUserId();
|
||||
|
||||
List<FriendshipDto> dtos = friendshipRepository.findFriends(myId, Status.ACCEPTED).stream()
|
||||
.map(f -> {
|
||||
UUID friendId = f.getSenderId().equals(myId) ? f.getReceiverId() : f.getSenderId();
|
||||
return userRepository.findById(friendId)
|
||||
.map(u -> new FriendshipDto(
|
||||
f.getFriendshipId(),
|
||||
new UserProfile(u.getUserId(), u.getName(), u.getProfilePicture(), u.getProfilePictureHq(), "FRIEND"),
|
||||
f.getStatus().name(),
|
||||
f.getCreatedAt()))
|
||||
.orElse(null);
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.toList();
|
||||
return ResponseEntity.ok(dtos);
|
||||
}
|
||||
|
||||
@GetMapping("/friends/pending")
|
||||
public ResponseEntity<List<FriendshipDto>> getPendingRequests(Principal principal) {
|
||||
UUID myId = userService.requireUser(principal).getUserId();
|
||||
|
||||
List<FriendshipDto> dtos = friendshipRepository.findByReceiverIdAndStatus(myId, Status.PENDING).stream()
|
||||
.map(f -> userRepository.findById(f.getSenderId())
|
||||
.map(u -> new FriendshipDto(
|
||||
f.getFriendshipId(),
|
||||
new UserProfile(u.getUserId(), u.getName(), u.getProfilePicture(), u.getProfilePictureHq(), "PENDING_RECEIVED"),
|
||||
f.getStatus().name(),
|
||||
f.getCreatedAt()))
|
||||
.orElse(null))
|
||||
.filter(Objects::nonNull)
|
||||
.toList();
|
||||
return ResponseEntity.ok(dtos);
|
||||
}
|
||||
|
||||
@GetMapping("/friends/pending/count")
|
||||
public ResponseEntity<Long> getPendingCount(Principal principal) {
|
||||
UUID myId = userService.requireUser(principal).getUserId();
|
||||
return ResponseEntity.ok(friendshipRepository.countByReceiverIdAndStatus(myId, Status.PENDING));
|
||||
}
|
||||
|
||||
// ── Messages ──
|
||||
|
||||
@PostMapping("/messages")
|
||||
public ResponseEntity<Void> sendMessage(@RequestBody SendMessageBody body, Principal principal) {
|
||||
UUID myId = userService.requireUser(principal).getUserId();
|
||||
|
||||
if (body.text() == null || body.text().isBlank()) return ResponseEntity.badRequest().build();
|
||||
|
||||
// Nachrichten an den Support-Account sind nicht erlaubt
|
||||
if (SupportUserService.SUPPORT_USER_ID.equals(body.receiverId())) {
|
||||
return ResponseEntity.status(403).build();
|
||||
}
|
||||
|
||||
MessageEntity msg = new MessageEntity();
|
||||
msg.setMessageId(UUID.randomUUID());
|
||||
msg.setSenderId(myId);
|
||||
msg.setReceiverId(body.receiverId());
|
||||
msg.setText(body.text().trim());
|
||||
msg.setSentAt(LocalDateTime.now());
|
||||
messageRepository.save(msg);
|
||||
LOGGER.debug("User {} hat Nachricht an User {} gesendet", myId, body.receiverId());
|
||||
long unread = messageRepository.countUnread(body.receiverId());
|
||||
sseService.push(body.receiverId(), "DM", Map.of("unreadCount", unread, "senderId", myId.toString()));
|
||||
return ResponseEntity.status(201).build();
|
||||
}
|
||||
|
||||
@GetMapping("/messages")
|
||||
public ResponseEntity<List<ConversationSummary>> getConversations(Principal principal) {
|
||||
UUID myId = userService.requireUser(principal).getUserId();
|
||||
|
||||
List<MessageEntity> allMessages = messageRepository.findAllByUser(myId);
|
||||
|
||||
// Group by partner, keep most recent message per partner
|
||||
Map<UUID, MessageEntity> latestByPartner = new LinkedHashMap<>();
|
||||
for (MessageEntity m : allMessages) {
|
||||
UUID partnerId = m.getSenderId().equals(myId) ? m.getReceiverId() : m.getSenderId();
|
||||
latestByPartner.putIfAbsent(partnerId, m);
|
||||
}
|
||||
|
||||
List<ConversationSummary> summaries = new ArrayList<>();
|
||||
for (Map.Entry<UUID, MessageEntity> entry : latestByPartner.entrySet()) {
|
||||
UUID partnerId = entry.getKey();
|
||||
MessageEntity lastMsg = entry.getValue();
|
||||
var partnerOpt = userRepository.findById(partnerId);
|
||||
if (partnerOpt.isEmpty()) continue;
|
||||
|
||||
UserProfile partnerProfile = toUserProfileWithStatus(partnerOpt.get(), myId);
|
||||
MessageDto lastMsgDto = toMessageDto(lastMsg);
|
||||
long unreadCount = allMessages.stream()
|
||||
.filter(m -> m.getSenderId().equals(partnerId)
|
||||
&& m.getReceiverId().equals(myId)
|
||||
&& m.getReadAt() == null)
|
||||
.count();
|
||||
summaries.add(new ConversationSummary(partnerProfile, lastMsgDto, unreadCount));
|
||||
}
|
||||
return ResponseEntity.ok(summaries);
|
||||
}
|
||||
|
||||
@GetMapping("/messages/unread/count")
|
||||
public ResponseEntity<Long> getUnreadCount(Principal principal) {
|
||||
UUID myId = userService.requireUser(principal).getUserId();
|
||||
return ResponseEntity.ok(messageRepository.countUnread(myId));
|
||||
}
|
||||
|
||||
private static final int MSG_PAGE_SIZE = 20;
|
||||
|
||||
@GetMapping("/messages/{partnerId}")
|
||||
public ResponseEntity<?> getConversation(
|
||||
@PathVariable UUID partnerId,
|
||||
@RequestParam(required = false) String before,
|
||||
@RequestParam(required = false) String after,
|
||||
Principal principal) {
|
||||
UUID myId = userService.requireUser(principal).getUserId();
|
||||
|
||||
if (after != null) {
|
||||
LocalDateTime afterDt = LocalDateTime.parse(after);
|
||||
List<MessageEntity> newMsgs = messageRepository.findConversationAfter(myId, partnerId, afterDt);
|
||||
messageRepository.markAsRead(myId, partnerId, LocalDateTime.now());
|
||||
return ResponseEntity.ok(Map.of("messages", newMsgs.stream().map(this::toMessageDto).toList(), "hasMore", false));
|
||||
}
|
||||
|
||||
List<MessageEntity> messages;
|
||||
if (before != null) {
|
||||
LocalDateTime beforeDt = LocalDateTime.parse(before);
|
||||
messages = new ArrayList<>(messageRepository.findConversationBefore(myId, partnerId, beforeDt, PageRequest.of(0, MSG_PAGE_SIZE + 1)));
|
||||
} else {
|
||||
messages = new ArrayList<>(messageRepository.findConversation(myId, partnerId, PageRequest.of(0, MSG_PAGE_SIZE + 1)));
|
||||
messageRepository.markAsRead(myId, partnerId, LocalDateTime.now());
|
||||
}
|
||||
|
||||
boolean hasMore = messages.size() > MSG_PAGE_SIZE;
|
||||
if (hasMore) messages = messages.subList(0, MSG_PAGE_SIZE);
|
||||
|
||||
// DESC order from DB → reverse to oldest-first for client
|
||||
Collections.reverse(messages);
|
||||
return ResponseEntity.ok(Map.of("messages", messages.stream().map(this::toMessageDto).toList(), "hasMore", hasMore));
|
||||
}
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
private UserProfile toUserProfileWithStatus(UserEntity user, UUID myId) {
|
||||
boolean isOwn = user.getUserId().equals(myId);
|
||||
String status = "NONE";
|
||||
if (!isOwn) {
|
||||
var existing = friendshipRepository.findExisting(myId, user.getUserId());
|
||||
if (existing.isPresent()) {
|
||||
FriendshipEntity f = existing.get();
|
||||
if (f.getStatus() == Status.ACCEPTED) {
|
||||
status = "FRIEND";
|
||||
} else if (f.getSenderId().equals(myId)) {
|
||||
status = "PENDING_SENT";
|
||||
} else {
|
||||
status = "PENDING_RECEIVED";
|
||||
}
|
||||
}
|
||||
}
|
||||
boolean isFriend = isOwn || "FRIEND".equals(status);
|
||||
|
||||
// Grunddaten nur zurückgeben wenn berechtigt
|
||||
de.oaa.xxx.user.Sichtbarkeit svGd = user.getSichtbarkeitGrunddaten();
|
||||
boolean showGrunddaten = isOwn || svGd == de.oaa.xxx.user.Sichtbarkeit.ALLE
|
||||
|| (svGd == de.oaa.xxx.user.Sichtbarkeit.NUR_FREUNDE && isFriend);
|
||||
|
||||
// XP nur zurückgeben wenn berechtigt
|
||||
de.oaa.xxx.user.Sichtbarkeit svXp = user.getSichtbarkeitXp();
|
||||
boolean showXp = isOwn || svXp == de.oaa.xxx.user.Sichtbarkeit.ALLE
|
||||
|| (svXp == de.oaa.xxx.user.Sichtbarkeit.NUR_FREUNDE && isFriend);
|
||||
|
||||
return new UserProfile(
|
||||
user.getUserId(), user.getName(), user.getProfilePicture(), user.getProfilePictureHq(),
|
||||
status,
|
||||
showGrunddaten ? user.getAlter() : null,
|
||||
showGrunddaten ? user.getGroesse() : null,
|
||||
showGrunddaten ? user.getGewicht() : null,
|
||||
showGrunddaten ? user.getGeschlecht() : null,
|
||||
showGrunddaten ? user.getNeigung() : null,
|
||||
showGrunddaten ? user.getBeziehungsstatus() : null,
|
||||
showGrunddaten ? user.getBeschreibung() : null,
|
||||
showXp ? user.getLockeeXp() : 0,
|
||||
showXp ? user.getKeyholderXp() : 0,
|
||||
showXp ? user.getBdsmXp() : 0,
|
||||
user.getSichtbarkeitGrunddaten(),
|
||||
user.getSichtbarkeitGalerie(),
|
||||
user.getSichtbarkeitFreunde(),
|
||||
user.getSichtbarkeitFeed(),
|
||||
user.getSichtbarkeitPinnwand(),
|
||||
user.getSichtbarkeitXp(),
|
||||
user.getSichtbarkeitLockhistorie(),
|
||||
user.getSichtbarkeitVorlieben(),
|
||||
user.isProfilBeiVeroeffentlichungenSichtbar());
|
||||
}
|
||||
|
||||
private MessageDto toMessageDto(MessageEntity m) {
|
||||
String senderName = userRepository.findById(m.getSenderId())
|
||||
.map(UserEntity::getName)
|
||||
.orElse("Unbekannt");
|
||||
return new MessageDto(
|
||||
m.getMessageId(), m.getSenderId(), senderName,
|
||||
m.getReceiverId(), m.getText(), m.getSentAt(), m.getReadAt() != null);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user