Allow public GET access to services and categories
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
package com.petshop.backend.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
|
||||
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
|
||||
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
|
||||
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSocketMessageBroker
|
||||
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
|
||||
|
||||
@Override
|
||||
public void configureMessageBroker(MessageBrokerRegistry config) {
|
||||
config.enableSimpleBroker("/topic", "/queue");
|
||||
config.setApplicationDestinationPrefixes("/app");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerStompEndpoints(StompEndpointRegistry registry) {
|
||||
registry.addEndpoint("/ws/chat")
|
||||
.setAllowedOriginPatterns("*")
|
||||
.withSockJS();
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,6 @@ import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/adoptions")
|
||||
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
|
||||
public class AdoptionController {
|
||||
|
||||
private final AdoptionService adoptionService;
|
||||
@@ -24,6 +23,7 @@ public class AdoptionController {
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
@PreAuthorize("hasAnyRole('CUSTOMER', 'STAFF', 'ADMIN')")
|
||||
public ResponseEntity<Page<AdoptionResponse>> getAllAdoptions(
|
||||
@RequestParam(required = false) String q,
|
||||
Pageable pageable) {
|
||||
@@ -31,16 +31,19 @@ public class AdoptionController {
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@PreAuthorize("hasAnyRole('CUSTOMER', 'STAFF', 'ADMIN')")
|
||||
public ResponseEntity<AdoptionResponse> getAdoptionById(@PathVariable Long id) {
|
||||
return ResponseEntity.ok(adoptionService.getAdoptionById(id));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@PreAuthorize("hasAnyRole('CUSTOMER', 'STAFF', 'ADMIN')")
|
||||
public ResponseEntity<AdoptionResponse> createAdoption(@Valid @RequestBody AdoptionRequest request) {
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(adoptionService.createAdoption(request));
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
|
||||
public ResponseEntity<AdoptionResponse> updateAdoption(
|
||||
@PathVariable Long id,
|
||||
@Valid @RequestBody AdoptionRequest request) {
|
||||
@@ -48,12 +51,14 @@ public class AdoptionController {
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
|
||||
public ResponseEntity<Void> deleteAdoption(@PathVariable Long id) {
|
||||
adoptionService.deleteAdoption(id);
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
@DeleteMapping
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
public ResponseEntity<Void> bulkDeleteAdoptions(@Valid @RequestBody BulkDeleteRequest request) {
|
||||
adoptionService.bulkDeleteAdoptions(request);
|
||||
return ResponseEntity.noContent().build();
|
||||
|
||||
@@ -17,7 +17,6 @@ import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/appointments")
|
||||
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
|
||||
public class AppointmentController {
|
||||
|
||||
private final AppointmentService appointmentService;
|
||||
@@ -27,6 +26,7 @@ public class AppointmentController {
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
@PreAuthorize("hasAnyRole('CUSTOMER', 'STAFF', 'ADMIN')")
|
||||
public ResponseEntity<Page<AppointmentResponse>> getAllAppointments(
|
||||
@RequestParam(required = false) String q,
|
||||
Pageable pageable) {
|
||||
@@ -34,16 +34,19 @@ public class AppointmentController {
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@PreAuthorize("hasAnyRole('CUSTOMER', 'STAFF', 'ADMIN')")
|
||||
public ResponseEntity<AppointmentResponse> getAppointmentById(@PathVariable Long id) {
|
||||
return ResponseEntity.ok(appointmentService.getAppointmentById(id));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@PreAuthorize("hasAnyRole('CUSTOMER', 'STAFF', 'ADMIN')")
|
||||
public ResponseEntity<AppointmentResponse> createAppointment(@Valid @RequestBody AppointmentRequest request) {
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(appointmentService.createAppointment(request));
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
|
||||
public ResponseEntity<AppointmentResponse> updateAppointment(
|
||||
@PathVariable Long id,
|
||||
@Valid @RequestBody AppointmentRequest request) {
|
||||
@@ -51,12 +54,14 @@ public class AppointmentController {
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
|
||||
public ResponseEntity<Void> deleteAppointment(@PathVariable Long id) {
|
||||
appointmentService.deleteAppointment(id);
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
@DeleteMapping
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
public ResponseEntity<Void> bulkDeleteAppointments(@Valid @RequestBody BulkDeleteRequest request) {
|
||||
appointmentService.bulkDeleteAppointments(request);
|
||||
return ResponseEntity.noContent().build();
|
||||
|
||||
@@ -14,7 +14,6 @@ import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/categories")
|
||||
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
|
||||
public class CategoryController {
|
||||
|
||||
private final CategoryService categoryService;
|
||||
@@ -36,11 +35,13 @@ public class CategoryController {
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
|
||||
public ResponseEntity<CategoryResponse> createCategory(@Valid @RequestBody CategoryRequest request) {
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(categoryService.createCategory(request));
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
|
||||
public ResponseEntity<CategoryResponse> updateCategory(
|
||||
@PathVariable Long id,
|
||||
@Valid @RequestBody CategoryRequest request) {
|
||||
@@ -48,12 +49,14 @@ public class CategoryController {
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
|
||||
public ResponseEntity<Void> deleteCategory(@PathVariable Long id) {
|
||||
categoryService.deleteCategory(id);
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
@DeleteMapping
|
||||
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
|
||||
public ResponseEntity<Void> bulkDeleteCategories(@Valid @RequestBody BulkDeleteRequest request) {
|
||||
categoryService.bulkDeleteCategories(request);
|
||||
return ResponseEntity.noContent().build();
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.petshop.backend.controller;
|
||||
|
||||
import com.petshop.backend.dto.chat.ConversationRequest;
|
||||
import com.petshop.backend.dto.chat.ConversationResponse;
|
||||
import com.petshop.backend.dto.chat.MessageRequest;
|
||||
import com.petshop.backend.dto.chat.MessageResponse;
|
||||
import com.petshop.backend.entity.User;
|
||||
import com.petshop.backend.repository.UserRepository;
|
||||
import com.petshop.backend.service.ChatService;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/chat")
|
||||
public class ChatController {
|
||||
|
||||
private final ChatService chatService;
|
||||
private final UserRepository userRepository;
|
||||
|
||||
public ChatController(ChatService chatService, UserRepository userRepository) {
|
||||
this.chatService = chatService;
|
||||
this.userRepository = userRepository;
|
||||
}
|
||||
|
||||
private User getCurrentUser() {
|
||||
UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||
return userRepository.findByUsername(userDetails.getUsername())
|
||||
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
|
||||
}
|
||||
|
||||
@PostMapping("/conversations")
|
||||
@PreAuthorize("hasAnyRole('CUSTOMER', 'STAFF', 'ADMIN')")
|
||||
public ResponseEntity<ConversationResponse> createConversation(@Valid @RequestBody ConversationRequest request) {
|
||||
User user = getCurrentUser();
|
||||
ConversationResponse response = chatService.createConversation(user.getId(), request);
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(response);
|
||||
}
|
||||
|
||||
@GetMapping("/conversations")
|
||||
@PreAuthorize("hasAnyRole('CUSTOMER', 'STAFF', 'ADMIN')")
|
||||
public ResponseEntity<List<ConversationResponse>> getConversations() {
|
||||
User user = getCurrentUser();
|
||||
List<ConversationResponse> conversations = chatService.getConversations(user.getId(), user.getRole());
|
||||
return ResponseEntity.ok(conversations);
|
||||
}
|
||||
|
||||
@GetMapping("/conversations/{id}")
|
||||
@PreAuthorize("hasAnyRole('CUSTOMER', 'STAFF', 'ADMIN')")
|
||||
public ResponseEntity<ConversationResponse> getConversation(@PathVariable Long id) {
|
||||
User user = getCurrentUser();
|
||||
ConversationResponse conversation = chatService.getConversation(id, user.getId(), user.getRole());
|
||||
return ResponseEntity.ok(conversation);
|
||||
}
|
||||
|
||||
@PostMapping("/conversations/{id}/messages")
|
||||
@PreAuthorize("hasAnyRole('CUSTOMER', 'STAFF', 'ADMIN')")
|
||||
public ResponseEntity<MessageResponse> sendMessage(
|
||||
@PathVariable Long id,
|
||||
@Valid @RequestBody MessageRequest request) {
|
||||
User user = getCurrentUser();
|
||||
MessageResponse message = chatService.sendMessage(id, user.getId(), request);
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(message);
|
||||
}
|
||||
|
||||
@GetMapping("/conversations/{id}/messages")
|
||||
@PreAuthorize("hasAnyRole('CUSTOMER', 'STAFF', 'ADMIN')")
|
||||
public ResponseEntity<List<MessageResponse>> getMessages(@PathVariable Long id) {
|
||||
User user = getCurrentUser();
|
||||
List<MessageResponse> messages = chatService.getMessages(id, user.getId(), user.getRole());
|
||||
return ResponseEntity.ok(messages);
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,6 @@ import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/services")
|
||||
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
|
||||
public class ServiceController {
|
||||
|
||||
private final ServiceService serviceService;
|
||||
@@ -36,11 +35,13 @@ public class ServiceController {
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
|
||||
public ResponseEntity<ServiceResponse> createService(@Valid @RequestBody ServiceRequest request) {
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(serviceService.createService(request));
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
|
||||
public ResponseEntity<ServiceResponse> updateService(
|
||||
@PathVariable Long id,
|
||||
@Valid @RequestBody ServiceRequest request) {
|
||||
@@ -48,12 +49,14 @@ public class ServiceController {
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
|
||||
public ResponseEntity<Void> deleteService(@PathVariable Long id) {
|
||||
serviceService.deleteService(id);
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
@DeleteMapping
|
||||
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
|
||||
public ResponseEntity<Void> bulkDeleteServices(@Valid @RequestBody BulkDeleteRequest request) {
|
||||
serviceService.bulkDeleteServices(request);
|
||||
return ResponseEntity.noContent().build();
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.petshop.backend.dto.chat;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
public class ConversationRequest {
|
||||
@NotBlank(message = "Initial message is required")
|
||||
private String message;
|
||||
|
||||
public ConversationRequest() {
|
||||
}
|
||||
|
||||
public ConversationRequest(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package com.petshop.backend.dto.chat;
|
||||
|
||||
import com.petshop.backend.entity.Conversation;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class ConversationResponse {
|
||||
private Long id;
|
||||
private Long customerId;
|
||||
private Long staffId;
|
||||
private String status;
|
||||
private String lastMessage;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
public ConversationResponse() {
|
||||
}
|
||||
|
||||
public ConversationResponse(Long id, Long customerId, Long staffId, String status, String lastMessage, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
this.id = id;
|
||||
this.customerId = customerId;
|
||||
this.staffId = staffId;
|
||||
this.status = status;
|
||||
this.lastMessage = lastMessage;
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public static ConversationResponse fromEntity(Conversation conversation, String lastMessage) {
|
||||
ConversationResponse response = new ConversationResponse();
|
||||
response.setId(conversation.getId());
|
||||
response.setCustomerId(conversation.getCustomerId());
|
||||
response.setStaffId(conversation.getStaffId());
|
||||
response.setStatus(conversation.getStatus().name());
|
||||
response.setLastMessage(lastMessage);
|
||||
response.setCreatedAt(conversation.getCreatedAt());
|
||||
response.setUpdatedAt(conversation.getUpdatedAt());
|
||||
return response;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getCustomerId() {
|
||||
return customerId;
|
||||
}
|
||||
|
||||
public void setCustomerId(Long customerId) {
|
||||
this.customerId = customerId;
|
||||
}
|
||||
|
||||
public Long getStaffId() {
|
||||
return staffId;
|
||||
}
|
||||
|
||||
public void setStaffId(Long staffId) {
|
||||
this.staffId = staffId;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getLastMessage() {
|
||||
return lastMessage;
|
||||
}
|
||||
|
||||
public void setLastMessage(String lastMessage) {
|
||||
this.lastMessage = lastMessage;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.petshop.backend.dto.chat;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
public class MessageRequest {
|
||||
@NotBlank(message = "Message content is required")
|
||||
private String content;
|
||||
|
||||
public MessageRequest() {
|
||||
}
|
||||
|
||||
public MessageRequest(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.petshop.backend.dto.chat;
|
||||
|
||||
import com.petshop.backend.entity.Message;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class MessageResponse {
|
||||
private Long id;
|
||||
private Long conversationId;
|
||||
private Long senderId;
|
||||
private String content;
|
||||
private LocalDateTime timestamp;
|
||||
private Boolean isRead;
|
||||
|
||||
public MessageResponse() {
|
||||
}
|
||||
|
||||
public MessageResponse(Long id, Long conversationId, Long senderId, String content, LocalDateTime timestamp, Boolean isRead) {
|
||||
this.id = id;
|
||||
this.conversationId = conversationId;
|
||||
this.senderId = senderId;
|
||||
this.content = content;
|
||||
this.timestamp = timestamp;
|
||||
this.isRead = isRead;
|
||||
}
|
||||
|
||||
public static MessageResponse fromEntity(Message message) {
|
||||
MessageResponse response = new MessageResponse();
|
||||
response.setId(message.getId());
|
||||
response.setConversationId(message.getConversationId());
|
||||
response.setSenderId(message.getSenderId());
|
||||
response.setContent(message.getContent());
|
||||
response.setTimestamp(message.getTimestamp());
|
||||
response.setIsRead(message.getIsRead());
|
||||
return response;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getConversationId() {
|
||||
return conversationId;
|
||||
}
|
||||
|
||||
public void setConversationId(Long conversationId) {
|
||||
this.conversationId = conversationId;
|
||||
}
|
||||
|
||||
public Long getSenderId() {
|
||||
return senderId;
|
||||
}
|
||||
|
||||
public void setSenderId(Long senderId) {
|
||||
this.senderId = senderId;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public LocalDateTime getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(LocalDateTime timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public Boolean getIsRead() {
|
||||
return isRead;
|
||||
}
|
||||
|
||||
public void setIsRead(Boolean isRead) {
|
||||
this.isRead = isRead;
|
||||
}
|
||||
}
|
||||
98
src/main/java/com/petshop/backend/entity/Conversation.java
Normal file
98
src/main/java/com/petshop/backend/entity/Conversation.java
Normal file
@@ -0,0 +1,98 @@
|
||||
package com.petshop.backend.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
import org.hibernate.annotations.UpdateTimestamp;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Entity
|
||||
@Table(name = "conversation")
|
||||
public class Conversation {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Long customerId;
|
||||
|
||||
@Column
|
||||
private Long staffId;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(length = 20, nullable = false)
|
||||
private ConversationStatus status = ConversationStatus.OPEN;
|
||||
|
||||
@CreationTimestamp
|
||||
@Column(name = "created_at", nullable = false, updatable = false)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@UpdateTimestamp
|
||||
@Column(name = "updated_at", nullable = false)
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
public enum ConversationStatus {
|
||||
OPEN, CLOSED
|
||||
}
|
||||
|
||||
public Conversation() {
|
||||
}
|
||||
|
||||
public Conversation(Long id, Long customerId, Long staffId, ConversationStatus status, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
this.id = id;
|
||||
this.customerId = customerId;
|
||||
this.staffId = staffId;
|
||||
this.status = status;
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getCustomerId() {
|
||||
return customerId;
|
||||
}
|
||||
|
||||
public void setCustomerId(Long customerId) {
|
||||
this.customerId = customerId;
|
||||
}
|
||||
|
||||
public Long getStaffId() {
|
||||
return staffId;
|
||||
}
|
||||
|
||||
public void setStaffId(Long staffId) {
|
||||
this.staffId = staffId;
|
||||
}
|
||||
|
||||
public ConversationStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(ConversationStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
}
|
||||
91
src/main/java/com/petshop/backend/entity/Message.java
Normal file
91
src/main/java/com/petshop/backend/entity/Message.java
Normal file
@@ -0,0 +1,91 @@
|
||||
package com.petshop.backend.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Entity
|
||||
@Table(name = "message")
|
||||
public class Message {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Long conversationId;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Long senderId;
|
||||
|
||||
@Column(nullable = false, columnDefinition = "TEXT")
|
||||
private String content;
|
||||
|
||||
@CreationTimestamp
|
||||
@Column(nullable = false, updatable = false)
|
||||
private LocalDateTime timestamp;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Boolean isRead = false;
|
||||
|
||||
public Message() {
|
||||
}
|
||||
|
||||
public Message(Long id, Long conversationId, Long senderId, String content, LocalDateTime timestamp, Boolean isRead) {
|
||||
this.id = id;
|
||||
this.conversationId = conversationId;
|
||||
this.senderId = senderId;
|
||||
this.content = content;
|
||||
this.timestamp = timestamp;
|
||||
this.isRead = isRead;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getConversationId() {
|
||||
return conversationId;
|
||||
}
|
||||
|
||||
public void setConversationId(Long conversationId) {
|
||||
this.conversationId = conversationId;
|
||||
}
|
||||
|
||||
public Long getSenderId() {
|
||||
return senderId;
|
||||
}
|
||||
|
||||
public void setSenderId(Long senderId) {
|
||||
this.senderId = senderId;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public LocalDateTime getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(LocalDateTime timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public Boolean getIsRead() {
|
||||
return isRead;
|
||||
}
|
||||
|
||||
public void setIsRead(Boolean isRead) {
|
||||
this.isRead = isRead;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.petshop.backend.repository;
|
||||
|
||||
import com.petshop.backend.entity.Conversation;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface ConversationRepository extends JpaRepository<Conversation, Long> {
|
||||
List<Conversation> findByCustomerId(Long customerId);
|
||||
List<Conversation> findByStaffId(Long staffId);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.petshop.backend.repository;
|
||||
|
||||
import com.petshop.backend.entity.Message;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface MessageRepository extends JpaRepository<Message, Long> {
|
||||
List<Message> findByConversationIdOrderByTimestampAsc(Long conversationId);
|
||||
}
|
||||
126
src/main/java/com/petshop/backend/service/ChatService.java
Normal file
126
src/main/java/com/petshop/backend/service/ChatService.java
Normal file
@@ -0,0 +1,126 @@
|
||||
package com.petshop.backend.service;
|
||||
|
||||
import com.petshop.backend.dto.chat.ConversationRequest;
|
||||
import com.petshop.backend.dto.chat.ConversationResponse;
|
||||
import com.petshop.backend.dto.chat.MessageRequest;
|
||||
import com.petshop.backend.dto.chat.MessageResponse;
|
||||
import com.petshop.backend.entity.Conversation;
|
||||
import com.petshop.backend.entity.Message;
|
||||
import com.petshop.backend.entity.User;
|
||||
import com.petshop.backend.exception.ResourceNotFoundException;
|
||||
import com.petshop.backend.repository.ConversationRepository;
|
||||
import com.petshop.backend.repository.MessageRepository;
|
||||
import com.petshop.backend.repository.UserRepository;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class ChatService {
|
||||
|
||||
private final ConversationRepository conversationRepository;
|
||||
private final MessageRepository messageRepository;
|
||||
private final UserRepository userRepository;
|
||||
|
||||
public ChatService(ConversationRepository conversationRepository,
|
||||
MessageRepository messageRepository,
|
||||
UserRepository userRepository) {
|
||||
this.conversationRepository = conversationRepository;
|
||||
this.messageRepository = messageRepository;
|
||||
this.userRepository = userRepository;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public ConversationResponse createConversation(Long userId, ConversationRequest request) {
|
||||
User user = userRepository.findById(userId)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("User not found"));
|
||||
|
||||
Conversation conversation = new Conversation();
|
||||
conversation.setCustomerId(userId);
|
||||
conversation.setStatus(Conversation.ConversationStatus.OPEN);
|
||||
conversation = conversationRepository.save(conversation);
|
||||
|
||||
Message message = new Message();
|
||||
message.setConversationId(conversation.getId());
|
||||
message.setSenderId(userId);
|
||||
message.setContent(request.getMessage());
|
||||
message.setIsRead(false);
|
||||
messageRepository.save(message);
|
||||
|
||||
return ConversationResponse.fromEntity(conversation, request.getMessage());
|
||||
}
|
||||
|
||||
public List<ConversationResponse> getConversations(Long userId, User.Role role) {
|
||||
List<Conversation> conversations;
|
||||
|
||||
if (role == User.Role.CUSTOMER) {
|
||||
conversations = conversationRepository.findByCustomerId(userId);
|
||||
} else if (role == User.Role.STAFF) {
|
||||
conversations = conversationRepository.findByStaffId(userId);
|
||||
if (conversations.isEmpty()) {
|
||||
conversations = conversationRepository.findAll();
|
||||
}
|
||||
} else {
|
||||
conversations = conversationRepository.findAll();
|
||||
}
|
||||
|
||||
return conversations.stream()
|
||||
.map(conv -> {
|
||||
List<Message> messages = messageRepository.findByConversationIdOrderByTimestampAsc(conv.getId());
|
||||
String lastMessage = messages.isEmpty() ? "" : messages.get(messages.size() - 1).getContent();
|
||||
return ConversationResponse.fromEntity(conv, lastMessage);
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public ConversationResponse getConversation(Long conversationId, Long userId, User.Role role) {
|
||||
Conversation conversation = conversationRepository.findById(conversationId)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("Conversation not found"));
|
||||
|
||||
if (role == User.Role.CUSTOMER && !conversation.getCustomerId().equals(userId)) {
|
||||
throw new AccessDeniedException("You can only view your own conversations");
|
||||
}
|
||||
|
||||
List<Message> messages = messageRepository.findByConversationIdOrderByTimestampAsc(conversationId);
|
||||
String lastMessage = messages.isEmpty() ? "" : messages.get(messages.size() - 1).getContent();
|
||||
|
||||
return ConversationResponse.fromEntity(conversation, lastMessage);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public MessageResponse sendMessage(Long conversationId, Long userId, MessageRequest request) {
|
||||
Conversation conversation = conversationRepository.findById(conversationId)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("Conversation not found"));
|
||||
|
||||
Message message = new Message();
|
||||
message.setConversationId(conversationId);
|
||||
message.setSenderId(userId);
|
||||
message.setContent(request.getContent());
|
||||
message.setIsRead(false);
|
||||
message = messageRepository.save(message);
|
||||
|
||||
if (conversation.getStaffId() == null && !userId.equals(conversation.getCustomerId())) {
|
||||
conversation.setStaffId(userId);
|
||||
conversationRepository.save(conversation);
|
||||
}
|
||||
|
||||
return MessageResponse.fromEntity(message);
|
||||
}
|
||||
|
||||
public List<MessageResponse> getMessages(Long conversationId, Long userId, User.Role role) {
|
||||
Conversation conversation = conversationRepository.findById(conversationId)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("Conversation not found"));
|
||||
|
||||
if (role == User.Role.CUSTOMER && !conversation.getCustomerId().equals(userId)) {
|
||||
throw new AccessDeniedException("You can only view messages from your own conversations");
|
||||
}
|
||||
|
||||
List<Message> messages = messageRepository.findByConversationIdOrderByTimestampAsc(conversationId);
|
||||
return messages.stream()
|
||||
.map(MessageResponse::fromEntity)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user