sakthivikram/geo-location
0
1package com.georeport.service;2 3import com.georeport.dto.CommentResponse;4import com.georeport.dto.CreateCommentRequest;5import com.georeport.entity.Comment;6import com.georeport.entity.Issue;7import com.georeport.entity.User;8import com.georeport.exception.ResourceNotFoundException;9import com.georeport.repository.CommentRepository;10import com.georeport.repository.IssueRepository;11import org.springframework.beans.factory.annotation.Autowired;12import org.springframework.messaging.simp.SimpMessagingTemplate;13import org.springframework.stereotype.Service;14import org.springframework.transaction.annotation.Transactional;15 16import java.util.List;17import java.util.Objects;18import java.util.stream.Collectors;19 20/**21 * Service for managing comments on issues.22 */23@Service24public class CommentService {25 26 @Autowired27 private CommentRepository commentRepository;28 29 @Autowired30 private IssueRepository issueRepository;31 32 @Autowired33 private SimpMessagingTemplate messagingTemplate;34 35 @Autowired36 private NotificationService notificationService;37 38 /**39 * Add a new comment to an issue40 */41 @Transactional42 public CommentResponse addComment(Long issueId, CreateCommentRequest request, User author) {43 Objects.requireNonNull(issueId, "issueId must not be null");44 @SuppressWarnings("null")45 Issue issue = issueRepository.findById(issueId)46 .orElseThrow(() -> new ResourceNotFoundException("Issue not found with id: " + issueId));47 48 Comment comment = Comment.builder()49 .content(request.getContent())50 .issue(issue)51 .author(author)52 .isAdminReply(author.isAdmin())53 .build();54 55 // Handle reply to parent comment56 if (request.getParentId() != null) {57 Long parentId = request.getParentId();58 Comment parent = commentRepository.findById(parentId)59 .orElseThrow(() -> new ResourceNotFoundException("Parent comment not found"));60 comment.setParent(parent);61 }62 63 @SuppressWarnings("null")64 Comment saved = commentRepository.save(comment);65 if (saved == null) {66 throw new IllegalStateException("Saved comment is null");67 }68 69 // Notify issue reporter if admin commented70 if (author.isAdmin() && issue.getReporter() != null && !issue.getReporter().getId().equals(author.getId())) {71 notificationService.createNotification(72 issue.getReporter(),73 "New Response",74 "Admin responded to your issue: " + issue.getTitle(),75 "COMMENT",76 issue.getId());77 78 // WebSocket notification79 if (issue.getReporter().getId() != null) {80 @SuppressWarnings("null")81 Object payload = buildCommentNotification(saved, issue);82 messagingTemplate.convertAndSend(83 "/topic/notifications/" + issue.getReporter().getId(),84 payload);85 }86 }87 88 // Notify admin if citizen added comment89 if (!author.isAdmin()) {90 @SuppressWarnings("null")91 Object payload = buildCommentNotification(saved, issue);92 messagingTemplate.convertAndSend(93 "/topic/issues",94 payload);95 }96 97 return toResponse(saved);98 }99 100 /**101 * Get all comments for an issue (threaded structure)102 */103 public List<CommentResponse> getCommentsForIssue(Long issueId) {104 // Get only top-level comments, replies are nested105 List<Comment> topLevelComments = commentRepository.findTopLevelCommentsByIssueId(issueId);106 return topLevelComments.stream()107 .map(this::toResponseWithReplies)108 .collect(Collectors.toList());109 }110 111 /**112 * Count comments for an issue113 */114 public long getCommentCount(Long issueId) {115 return commentRepository.countByIssueId(issueId);116 }117 118 /**119 * Convert Comment entity to response DTO (without nested replies)120 */121 private CommentResponse toResponse(Comment comment) {122 return CommentResponse.builder()123 .id(comment.getId())124 .content(comment.getContent())125 .issueId(comment.getIssue().getId())126 .authorId(comment.getAuthor().getId())127 .authorName(comment.getAuthor().getFullName())128 .isAdminReply(comment.getIsAdminReply())129 .parentId(comment.getParent() != null ? comment.getParent().getId() : null)130 .createdAt(comment.getCreatedAt())131 .updatedAt(comment.getUpdatedAt())132 .build();133 }134 135 /**136 * Convert Comment entity to response DTO with nested replies137 */138 private CommentResponse toResponseWithReplies(Comment comment) {139 CommentResponse response = toResponse(comment);140 141 // Recursively load replies142 if (comment.getReplies() != null && !comment.getReplies().isEmpty()) {143 response.setReplies(144 comment.getReplies().stream()145 .map(this::toResponseWithReplies)146 .collect(Collectors.toList()));147 }148 149 return response;150 }151 152 /**153 * Build WebSocket notification payload for new comment154 */155 private Object buildCommentNotification(Comment comment, Issue issue) {156 return java.util.Map.of(157 "eventType", "NEW_COMMENT",158 "issueId", issue.getId(),159 "issueTitle", issue.getTitle(),160 "commentId", comment.getId(),161 "authorName", comment.getAuthor().getFullName(),162 "isAdminReply", comment.getIsAdminReply(),163 "preview", comment.getContent().substring(0, Math.min(100, comment.getContent().length())));164 }165}166 