package com.openisle.controller; import com.openisle.dto.*; import com.openisle.exception.NotFoundException; import com.openisle.mapper.TagMapper; import com.openisle.mapper.UserMapper; import com.openisle.model.User; import com.openisle.service.*; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.util.Map; @RestController @RequestMapping("/api/users") @RequiredArgsConstructor public class UserController { private final UserService userService; private final ImageUploader imageUploader; private final PostService postService; private final CommentService commentService; private final ReactionService reactionService; private final TagService tagService; private final SubscriptionService subscriptionService; private final LevelService levelService; private final JwtService jwtService; private final UserMapper userMapper; private final TagMapper tagMapper; @Value("${app.upload.check-type:true}") private boolean checkImageType; @Value("${app.upload.max-size:5242880}") private long maxUploadSize; @Value("${app.user.posts-limit:10}") private int defaultPostsLimit; @Value("${app.user.replies-limit:50}") private int defaultRepliesLimit; @Value("${app.user.tags-limit:50}") private int defaultTagsLimit; @GetMapping("/me") public ResponseEntity me(Authentication auth) { User user = userService.findByUsername(auth.getName()).orElseThrow(); return ResponseEntity.ok(userMapper.toDto(user, auth)); } @PostMapping("/me/avatar") public ResponseEntity uploadAvatar(@RequestParam("file") MultipartFile file, Authentication auth) { if (checkImageType && (file.getContentType() == null || !file.getContentType().startsWith("image/"))) { return ResponseEntity.badRequest().body(Map.of("error", "File is not an image")); } if (file.getSize() > maxUploadSize) { return ResponseEntity.badRequest().body(Map.of("error", "File too large")); } String url = null; try { url = imageUploader.upload(file.getBytes(), file.getOriginalFilename()).join(); } catch (IOException e) { return ResponseEntity.internalServerError().body(Map.of("url", url)); } userService.updateAvatar(auth.getName(), url); return ResponseEntity.ok(Map.of("url", url)); } @PutMapping("/me") public ResponseEntity updateProfile(@RequestBody UpdateProfileDto dto, Authentication auth) { User user = userService.updateProfile(auth.getName(), dto.getUsername(), dto.getIntroduction()); return ResponseEntity.ok(Map.of( "token", jwtService.generateToken(user.getUsername()), "user", userMapper.toDto(user, auth) )); } @PostMapping("/me/signin") public Map signIn(Authentication auth) { int reward = levelService.awardForSignin(auth.getName()); return Map.of("reward", reward); } @GetMapping("/{identifier}") public ResponseEntity getUser(@PathVariable("identifier") String identifier, Authentication auth) { User user = userService.findByIdentifier(identifier).orElseThrow(() -> new NotFoundException("User not found")); return ResponseEntity.ok(userMapper.toDto(user, auth)); } @GetMapping("/{identifier}/posts") public java.util.List userPosts(@PathVariable("identifier") String identifier, @RequestParam(value = "limit", required = false) Integer limit) { int l = limit != null ? limit : defaultPostsLimit; User user = userService.findByIdentifier(identifier).orElseThrow(); return postService.getRecentPostsByUser(user.getUsername(), l).stream() .map(userMapper::toMetaDto) .collect(java.util.stream.Collectors.toList()); } @GetMapping("/{identifier}/subscribed-posts") public java.util.List subscribedPosts(@PathVariable("identifier") String identifier, @RequestParam(value = "limit", required = false) Integer limit) { int l = limit != null ? limit : defaultPostsLimit; User user = userService.findByIdentifier(identifier).orElseThrow(); return subscriptionService.getSubscribedPosts(user.getUsername()).stream() .limit(l) .map(userMapper::toMetaDto) .collect(java.util.stream.Collectors.toList()); } @GetMapping("/{identifier}/replies") public java.util.List userReplies(@PathVariable("identifier") String identifier, @RequestParam(value = "limit", required = false) Integer limit) { int l = limit != null ? limit : defaultRepliesLimit; User user = userService.findByIdentifier(identifier).orElseThrow(); return commentService.getRecentCommentsByUser(user.getUsername(), l).stream() .map(userMapper::toCommentInfoDto) .collect(java.util.stream.Collectors.toList()); } @GetMapping("/{identifier}/hot-posts") public java.util.List hotPosts(@PathVariable("identifier") String identifier, @RequestParam(value = "limit", required = false) Integer limit) { int l = limit != null ? limit : 10; User user = userService.findByIdentifier(identifier).orElseThrow(); java.util.List ids = reactionService.topPostIds(user.getUsername(), l); return postService.getPostsByIds(ids).stream() .map(userMapper::toMetaDto) .collect(java.util.stream.Collectors.toList()); } @GetMapping("/{identifier}/hot-replies") public java.util.List hotReplies(@PathVariable("identifier") String identifier, @RequestParam(value = "limit", required = false) Integer limit) { int l = limit != null ? limit : 10; User user = userService.findByIdentifier(identifier).orElseThrow(); java.util.List ids = reactionService.topCommentIds(user.getUsername(), l); return commentService.getCommentsByIds(ids).stream() .map(userMapper::toCommentInfoDto) .collect(java.util.stream.Collectors.toList()); } @GetMapping("/{identifier}/hot-tags") public java.util.List hotTags(@PathVariable("identifier") String identifier, @RequestParam(value = "limit", required = false) Integer limit) { int l = limit != null ? limit : 10; User user = userService.findByIdentifier(identifier).orElseThrow(); return tagService.getTagsByUser(user.getUsername()).stream() .map(t -> tagMapper.toDto(t, postService.countPostsByTag(t.getId()))) .sorted((a, b) -> Long.compare(b.getCount(), a.getCount())) .limit(l) .collect(java.util.stream.Collectors.toList()); } @GetMapping("/{identifier}/tags") public java.util.List userTags(@PathVariable("identifier") String identifier, @RequestParam(value = "limit", required = false) Integer limit) { int l = limit != null ? limit : defaultTagsLimit; User user = userService.findByIdentifier(identifier).orElseThrow(); return tagService.getRecentTagsByUser(user.getUsername(), l).stream() .map(t -> tagMapper.toDto(t, postService.countPostsByTag(t.getId()))) .collect(java.util.stream.Collectors.toList()); } @GetMapping("/{identifier}/following") public java.util.List following(@PathVariable("identifier") String identifier) { User user = userService.findByIdentifier(identifier).orElseThrow(); return subscriptionService.getSubscribedUsers(user.getUsername()).stream() .map(userMapper::toDto) .collect(java.util.stream.Collectors.toList()); } @GetMapping("/{identifier}/followers") public java.util.List followers(@PathVariable("identifier") String identifier) { User user = userService.findByIdentifier(identifier).orElseThrow(); return subscriptionService.getSubscribers(user.getUsername()).stream() .map(userMapper::toDto) .collect(java.util.stream.Collectors.toList()); } @GetMapping("/admins") public java.util.List admins() { return userService.getAdmins().stream() .map(userMapper::toDto) .collect(java.util.stream.Collectors.toList()); } @GetMapping("/{identifier}/all") public ResponseEntity userAggregate(@PathVariable("identifier") String identifier, @RequestParam(value = "postsLimit", required = false) Integer postsLimit, @RequestParam(value = "repliesLimit", required = false) Integer repliesLimit, Authentication auth) { User user = userService.findByIdentifier(identifier).orElseThrow(); int pLimit = postsLimit != null ? postsLimit : defaultPostsLimit; int rLimit = repliesLimit != null ? repliesLimit : defaultRepliesLimit; java.util.List posts = postService.getRecentPostsByUser(user.getUsername(), pLimit).stream() .map(userMapper::toMetaDto) .collect(java.util.stream.Collectors.toList()); java.util.List replies = commentService.getRecentCommentsByUser(user.getUsername(), rLimit).stream() .map(userMapper::toCommentInfoDto) .collect(java.util.stream.Collectors.toList()); UserAggregateDto dto = new UserAggregateDto(); dto.setUser(userMapper.toDto(user, auth)); dto.setPosts(posts); dto.setReplies(replies); return ResponseEntity.ok(dto); } }