Add customer registration, profile management and refunds
This commit is contained in:
@@ -34,5 +34,15 @@ public class DataInitializer implements CommandLineRunner {
|
||||
staff.setRole(User.Role.STAFF);
|
||||
userRepository.save(staff);
|
||||
}
|
||||
|
||||
if (userRepository.findByUsername("customer").isEmpty()) {
|
||||
User customer = new User();
|
||||
customer.setUsername("customer");
|
||||
customer.setPassword(passwordEncoder.encode("customer123"));
|
||||
customer.setEmail("customer@petshop.com");
|
||||
customer.setFullName("Test Customer");
|
||||
customer.setRole(User.Role.CUSTOMER);
|
||||
userRepository.save(customer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package com.petshop.backend.controller;
|
||||
|
||||
import com.petshop.backend.dto.auth.AvatarUploadResponse;
|
||||
import com.petshop.backend.dto.auth.LoginRequest;
|
||||
import com.petshop.backend.dto.auth.LoginResponse;
|
||||
import com.petshop.backend.dto.auth.ProfileUpdateRequest;
|
||||
import com.petshop.backend.dto.auth.RegisterRequest;
|
||||
import com.petshop.backend.dto.auth.RegisterResponse;
|
||||
import com.petshop.backend.dto.auth.UserInfoResponse;
|
||||
import com.petshop.backend.entity.User;
|
||||
import com.petshop.backend.repository.UserRepository;
|
||||
@@ -18,9 +22,17 @@ import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/auth")
|
||||
@@ -38,6 +50,46 @@ public class AuthController {
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
}
|
||||
|
||||
@PostMapping("/register")
|
||||
public ResponseEntity<?> register(@Valid @RequestBody RegisterRequest request) {
|
||||
if (userRepository.findByUsername(request.getUsername()).isPresent()) {
|
||||
Map<String, String> error = new HashMap<>();
|
||||
error.put("message", "Username already exists");
|
||||
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
|
||||
}
|
||||
|
||||
if (userRepository.findByEmail(request.getEmail()).isPresent()) {
|
||||
Map<String, String> error = new HashMap<>();
|
||||
error.put("message", "Email already exists");
|
||||
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
|
||||
}
|
||||
|
||||
User user = new User();
|
||||
user.setUsername(request.getUsername());
|
||||
user.setPassword(passwordEncoder.encode(request.getPassword()));
|
||||
user.setEmail(request.getEmail());
|
||||
user.setFullName(request.getFullName());
|
||||
user.setRole(User.Role.CUSTOMER);
|
||||
|
||||
User savedUser = userRepository.save(user);
|
||||
|
||||
UserDetails userDetails = new org.springframework.security.core.userdetails.User(
|
||||
savedUser.getUsername(),
|
||||
savedUser.getPassword(),
|
||||
java.util.Collections.emptyList()
|
||||
);
|
||||
|
||||
String token = jwtUtil.generateToken(userDetails);
|
||||
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(new RegisterResponse(
|
||||
savedUser.getId(),
|
||||
savedUser.getUsername(),
|
||||
savedUser.getEmail(),
|
||||
savedUser.getRole().name(),
|
||||
token
|
||||
));
|
||||
}
|
||||
|
||||
@PostMapping("/login")
|
||||
public ResponseEntity<?> login(@Valid @RequestBody LoginRequest request) {
|
||||
try {
|
||||
@@ -80,10 +132,157 @@ public class AuthController {
|
||||
return ResponseEntity.ok(new UserInfoResponse(
|
||||
user.getId(),
|
||||
user.getUsername(),
|
||||
user.getEmail(),
|
||||
user.getFullName(),
|
||||
user.getAvatarUrl(),
|
||||
user.getRole().name()
|
||||
));
|
||||
}
|
||||
|
||||
@PutMapping("/me")
|
||||
public ResponseEntity<?> updateProfile(@Valid @RequestBody ProfileUpdateRequest request) {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
String username = authentication.getName();
|
||||
|
||||
User user = userRepository.findByUsername(username)
|
||||
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
|
||||
|
||||
if (request.getUsername() != null && !request.getUsername().equals(user.getUsername())) {
|
||||
if (userRepository.findByUsername(request.getUsername()).isPresent()) {
|
||||
Map<String, String> error = new HashMap<>();
|
||||
error.put("message", "Username already exists");
|
||||
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
|
||||
}
|
||||
user.setUsername(request.getUsername());
|
||||
}
|
||||
|
||||
if (request.getEmail() != null && !request.getEmail().equals(user.getEmail())) {
|
||||
if (userRepository.findByEmail(request.getEmail()).isPresent()) {
|
||||
Map<String, String> error = new HashMap<>();
|
||||
error.put("message", "Email already exists");
|
||||
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
|
||||
}
|
||||
user.setEmail(request.getEmail());
|
||||
}
|
||||
|
||||
if (request.getFullName() != null) {
|
||||
user.setFullName(request.getFullName());
|
||||
}
|
||||
|
||||
if (request.getPassword() != null && !request.getPassword().isEmpty()) {
|
||||
user.setPassword(passwordEncoder.encode(request.getPassword()));
|
||||
}
|
||||
|
||||
User updatedUser = userRepository.save(user);
|
||||
|
||||
return ResponseEntity.ok(new UserInfoResponse(
|
||||
updatedUser.getId(),
|
||||
updatedUser.getUsername(),
|
||||
updatedUser.getEmail(),
|
||||
updatedUser.getFullName(),
|
||||
updatedUser.getAvatarUrl(),
|
||||
updatedUser.getRole().name()
|
||||
));
|
||||
}
|
||||
|
||||
@PostMapping("/me/avatar")
|
||||
public ResponseEntity<?> uploadAvatar(@RequestParam("avatar") MultipartFile file) {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
String username = authentication.getName();
|
||||
|
||||
User user = userRepository.findByUsername(username)
|
||||
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
|
||||
|
||||
if (file.isEmpty()) {
|
||||
Map<String, String> error = new HashMap<>();
|
||||
error.put("message", "Please select a file to upload");
|
||||
return ResponseEntity.badRequest().body(error);
|
||||
}
|
||||
|
||||
if (file.getSize() > 5 * 1024 * 1024) {
|
||||
Map<String, String> error = new HashMap<>();
|
||||
error.put("message", "File size must not exceed 5MB");
|
||||
return ResponseEntity.badRequest().body(error);
|
||||
}
|
||||
|
||||
String contentType = file.getContentType();
|
||||
if (contentType == null || (!contentType.equals("image/jpeg") && !contentType.equals("image/png") && !contentType.equals("image/gif"))) {
|
||||
Map<String, String> error = new HashMap<>();
|
||||
error.put("message", "Only JPG, PNG, and GIF images are allowed");
|
||||
return ResponseEntity.badRequest().body(error);
|
||||
}
|
||||
|
||||
try {
|
||||
String uploadDir = "uploads/avatars";
|
||||
File directory = new File(uploadDir);
|
||||
if (!directory.exists()) {
|
||||
directory.mkdirs();
|
||||
}
|
||||
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
String extension = originalFilename != null && originalFilename.contains(".")
|
||||
? originalFilename.substring(originalFilename.lastIndexOf("."))
|
||||
: ".jpg";
|
||||
String filename = UUID.randomUUID().toString() + extension;
|
||||
Path filePath = Paths.get(uploadDir, filename);
|
||||
|
||||
Files.copy(file.getInputStream(), filePath, StandardCopyOption.REPLACE_EXISTING);
|
||||
|
||||
String avatarUrl = "/uploads/avatars/" + filename;
|
||||
user.setAvatarUrl(avatarUrl);
|
||||
userRepository.save(user);
|
||||
|
||||
return ResponseEntity.ok(new AvatarUploadResponse(avatarUrl, "Avatar uploaded successfully"));
|
||||
|
||||
} catch (IOException e) {
|
||||
Map<String, String> error = new HashMap<>();
|
||||
error.put("message", "Failed to upload avatar: " + e.getMessage());
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/me/avatar")
|
||||
public ResponseEntity<?> getAvatar() {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
String username = authentication.getName();
|
||||
|
||||
User user = userRepository.findByUsername(username)
|
||||
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
|
||||
|
||||
if (user.getAvatarUrl() == null || user.getAvatarUrl().isEmpty()) {
|
||||
Map<String, String> error = new HashMap<>();
|
||||
error.put("message", "No avatar uploaded");
|
||||
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
|
||||
}
|
||||
|
||||
Map<String, String> response = new HashMap<>();
|
||||
response.put("avatarUrl", user.getAvatarUrl());
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
@DeleteMapping("/me/avatar")
|
||||
public ResponseEntity<?> deleteAvatar() {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
String username = authentication.getName();
|
||||
|
||||
User user = userRepository.findByUsername(username)
|
||||
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
|
||||
|
||||
if (user.getAvatarUrl() != null && !user.getAvatarUrl().isEmpty()) {
|
||||
try {
|
||||
Path filePath = Paths.get("." + user.getAvatarUrl());
|
||||
Files.deleteIfExists(filePath);
|
||||
} catch (IOException e) {
|
||||
}
|
||||
user.setAvatarUrl(null);
|
||||
userRepository.save(user);
|
||||
}
|
||||
|
||||
Map<String, String> response = new HashMap<>();
|
||||
response.put("message", "Avatar deleted successfully");
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
@PostMapping("/logout")
|
||||
public ResponseEntity<?> logout() {
|
||||
Map<String, String> response = new HashMap<>();
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
package com.petshop.backend.controller;
|
||||
|
||||
import com.petshop.backend.dto.refund.RefundRequest;
|
||||
import com.petshop.backend.dto.refund.RefundResponse;
|
||||
import com.petshop.backend.dto.refund.RefundUpdateRequest;
|
||||
import com.petshop.backend.service.RefundService;
|
||||
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.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/refunds")
|
||||
public class RefundController {
|
||||
|
||||
private final RefundService refundService;
|
||||
|
||||
public RefundController(RefundService refundService) {
|
||||
this.refundService = refundService;
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@PreAuthorize("hasAnyRole('CUSTOMER', 'STAFF', 'ADMIN')")
|
||||
public ResponseEntity<?> createRefund(@Valid @RequestBody RefundRequest request) {
|
||||
try {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
String role = authentication.getAuthorities().stream()
|
||||
.findFirst()
|
||||
.map(authority -> authority.getAuthority().replace("ROLE_", ""))
|
||||
.orElse(null);
|
||||
|
||||
Long customerId = role != null && role.equals("CUSTOMER") ? 1L : null;
|
||||
|
||||
RefundResponse refund = refundService.createRefund(request, customerId);
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(refund);
|
||||
} catch (RuntimeException e) {
|
||||
Map<String, String> error = new HashMap<>();
|
||||
error.put("message", e.getMessage());
|
||||
return ResponseEntity.badRequest().body(error);
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
@PreAuthorize("hasAnyRole('CUSTOMER', 'STAFF', 'ADMIN')")
|
||||
public ResponseEntity<List<RefundResponse>> getAllRefunds() {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
String role = authentication.getAuthorities().stream()
|
||||
.findFirst()
|
||||
.map(authority -> authority.getAuthority().replace("ROLE_", ""))
|
||||
.orElse(null);
|
||||
|
||||
Long customerId = role != null && role.equals("CUSTOMER") ? 1L : null;
|
||||
|
||||
List<RefundResponse> refunds = refundService.getAllRefunds(customerId);
|
||||
return ResponseEntity.ok(refunds);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@PreAuthorize("hasAnyRole('CUSTOMER', 'STAFF', 'ADMIN')")
|
||||
public ResponseEntity<?> getRefundById(@PathVariable Long id) {
|
||||
try {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
String role = authentication.getAuthorities().stream()
|
||||
.findFirst()
|
||||
.map(authority -> authority.getAuthority().replace("ROLE_", ""))
|
||||
.orElse(null);
|
||||
|
||||
Long customerId = role != null && role.equals("CUSTOMER") ? 1L : null;
|
||||
|
||||
RefundResponse refund = refundService.getRefundById(id, customerId);
|
||||
return ResponseEntity.ok(refund);
|
||||
} catch (RuntimeException e) {
|
||||
Map<String, String> error = new HashMap<>();
|
||||
error.put("message", e.getMessage());
|
||||
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
|
||||
public ResponseEntity<?> updateRefund(@PathVariable Long id, @Valid @RequestBody RefundUpdateRequest request) {
|
||||
try {
|
||||
RefundResponse refund = refundService.updateRefundStatus(id, request.getStatus());
|
||||
return ResponseEntity.ok(refund);
|
||||
} catch (RuntimeException e) {
|
||||
Map<String, String> error = new HashMap<>();
|
||||
error.put("message", e.getMessage());
|
||||
return ResponseEntity.badRequest().body(error);
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
public ResponseEntity<?> deleteRefund(@PathVariable Long id) {
|
||||
try {
|
||||
refundService.deleteRefund(id);
|
||||
Map<String, String> response = new HashMap<>();
|
||||
response.put("message", "Refund deleted successfully");
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (RuntimeException e) {
|
||||
Map<String, String> error = new HashMap<>();
|
||||
error.put("message", e.getMessage());
|
||||
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.petshop.backend.dto.auth;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class AvatarUploadResponse {
|
||||
private String avatarUrl;
|
||||
private String message;
|
||||
|
||||
public AvatarUploadResponse() {
|
||||
}
|
||||
|
||||
public AvatarUploadResponse(String avatarUrl, String message) {
|
||||
this.avatarUrl = avatarUrl;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getAvatarUrl() {
|
||||
return avatarUrl;
|
||||
}
|
||||
|
||||
public void setAvatarUrl(String avatarUrl) {
|
||||
this.avatarUrl = avatarUrl;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
AvatarUploadResponse that = (AvatarUploadResponse) o;
|
||||
return Objects.equals(avatarUrl, that.avatarUrl) &&
|
||||
Objects.equals(message, that.message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(avatarUrl, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AvatarUploadResponse{" +
|
||||
"avatarUrl='" + avatarUrl + '\'' +
|
||||
", message='" + message + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.petshop.backend.dto.auth;
|
||||
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ProfileUpdateRequest {
|
||||
@Size(min = 3, max = 50, message = "Username must be between 3 and 50 characters")
|
||||
private String username;
|
||||
|
||||
@Email(message = "Email must be valid")
|
||||
private String email;
|
||||
|
||||
@Size(max = 100, message = "Full name must not exceed 100 characters")
|
||||
private String fullName;
|
||||
|
||||
@Size(min = 6, message = "Password must be at least 6 characters")
|
||||
private String password;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getFullName() {
|
||||
return fullName;
|
||||
}
|
||||
|
||||
public void setFullName(String fullName) {
|
||||
this.fullName = fullName;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ProfileUpdateRequest that = (ProfileUpdateRequest) o;
|
||||
return Objects.equals(username, that.username) &&
|
||||
Objects.equals(email, that.email) &&
|
||||
Objects.equals(fullName, that.fullName) &&
|
||||
Objects.equals(password, that.password);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(username, email, fullName, password);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ProfileUpdateRequest{" +
|
||||
"username='" + username + '\'' +
|
||||
", email='" + email + '\'' +
|
||||
", fullName='" + fullName + '\'' +
|
||||
", password='" + password + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.petshop.backend.dto.auth;
|
||||
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import java.util.Objects;
|
||||
|
||||
public class RegisterRequest {
|
||||
@NotBlank(message = "Username is required")
|
||||
@Size(min = 3, max = 50, message = "Username must be between 3 and 50 characters")
|
||||
private String username;
|
||||
|
||||
@NotBlank(message = "Password is required")
|
||||
@Size(min = 6, message = "Password must be at least 6 characters")
|
||||
private String password;
|
||||
|
||||
@NotBlank(message = "Email is required")
|
||||
@Email(message = "Email must be valid")
|
||||
private String email;
|
||||
|
||||
@NotBlank(message = "Full name is required")
|
||||
@Size(max = 100, message = "Full name must not exceed 100 characters")
|
||||
private String fullName;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getFullName() {
|
||||
return fullName;
|
||||
}
|
||||
|
||||
public void setFullName(String fullName) {
|
||||
this.fullName = fullName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
RegisterRequest that = (RegisterRequest) o;
|
||||
return Objects.equals(username, that.username) &&
|
||||
Objects.equals(password, that.password) &&
|
||||
Objects.equals(email, that.email) &&
|
||||
Objects.equals(fullName, that.fullName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(username, password, email, fullName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RegisterRequest{" +
|
||||
"username='" + username + '\'' +
|
||||
", password='" + password + '\'' +
|
||||
", email='" + email + '\'' +
|
||||
", fullName='" + fullName + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package com.petshop.backend.dto.auth;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class RegisterResponse {
|
||||
private Long id;
|
||||
private String username;
|
||||
private String email;
|
||||
private String role;
|
||||
private String token;
|
||||
|
||||
public RegisterResponse() {
|
||||
}
|
||||
|
||||
public RegisterResponse(Long id, String username, String email, String role, String token) {
|
||||
this.id = id;
|
||||
this.username = username;
|
||||
this.email = email;
|
||||
this.role = role;
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
public void setRole(String role) {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
RegisterResponse that = (RegisterResponse) o;
|
||||
return Objects.equals(id, that.id) &&
|
||||
Objects.equals(username, that.username) &&
|
||||
Objects.equals(email, that.email) &&
|
||||
Objects.equals(role, that.role) &&
|
||||
Objects.equals(token, that.token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, username, email, role, token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RegisterResponse{" +
|
||||
"id=" + id +
|
||||
", username='" + username + '\'' +
|
||||
", email='" + email + '\'' +
|
||||
", role='" + role + '\'' +
|
||||
", token='" + token + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -5,14 +5,20 @@ import java.util.Objects;
|
||||
public class UserInfoResponse {
|
||||
private Long id;
|
||||
private String username;
|
||||
private String email;
|
||||
private String fullName;
|
||||
private String avatarUrl;
|
||||
private String role;
|
||||
|
||||
public UserInfoResponse() {
|
||||
}
|
||||
|
||||
public UserInfoResponse(Long id, String username, String role) {
|
||||
public UserInfoResponse(Long id, String username, String email, String fullName, String avatarUrl, String role) {
|
||||
this.id = id;
|
||||
this.username = username;
|
||||
this.email = email;
|
||||
this.fullName = fullName;
|
||||
this.avatarUrl = avatarUrl;
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
@@ -32,6 +38,30 @@ public class UserInfoResponse {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getFullName() {
|
||||
return fullName;
|
||||
}
|
||||
|
||||
public void setFullName(String fullName) {
|
||||
this.fullName = fullName;
|
||||
}
|
||||
|
||||
public String getAvatarUrl() {
|
||||
return avatarUrl;
|
||||
}
|
||||
|
||||
public void setAvatarUrl(String avatarUrl) {
|
||||
this.avatarUrl = avatarUrl;
|
||||
}
|
||||
|
||||
public String getRole() {
|
||||
return role;
|
||||
}
|
||||
@@ -45,12 +75,17 @@ public class UserInfoResponse {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
UserInfoResponse that = (UserInfoResponse) o;
|
||||
return Objects.equals(id, that.id) && Objects.equals(username, that.username) && Objects.equals(role, that.role);
|
||||
return Objects.equals(id, that.id) &&
|
||||
Objects.equals(username, that.username) &&
|
||||
Objects.equals(email, that.email) &&
|
||||
Objects.equals(fullName, that.fullName) &&
|
||||
Objects.equals(avatarUrl, that.avatarUrl) &&
|
||||
Objects.equals(role, that.role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, username, role);
|
||||
return Objects.hash(id, username, email, fullName, avatarUrl, role);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -58,6 +93,9 @@ public class UserInfoResponse {
|
||||
return "UserInfoResponse{" +
|
||||
"id=" + id +
|
||||
", username='" + username + '\'' +
|
||||
", email='" + email + '\'' +
|
||||
", fullName='" + fullName + '\'' +
|
||||
", avatarUrl='" + avatarUrl + '\'' +
|
||||
", role='" + role + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.petshop.backend.dto.refund;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.util.Objects;
|
||||
|
||||
public class RefundRequest {
|
||||
@NotNull(message = "Sale ID is required")
|
||||
private Long saleId;
|
||||
|
||||
@NotBlank(message = "Reason is required")
|
||||
private String reason;
|
||||
|
||||
public Long getSaleId() {
|
||||
return saleId;
|
||||
}
|
||||
|
||||
public void setSaleId(Long saleId) {
|
||||
this.saleId = saleId;
|
||||
}
|
||||
|
||||
public String getReason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
public void setReason(String reason) {
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
RefundRequest that = (RefundRequest) o;
|
||||
return Objects.equals(saleId, that.saleId) &&
|
||||
Objects.equals(reason, that.reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(saleId, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RefundRequest{" +
|
||||
"saleId=" + saleId +
|
||||
", reason='" + reason + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
128
src/main/java/com/petshop/backend/dto/refund/RefundResponse.java
Normal file
128
src/main/java/com/petshop/backend/dto/refund/RefundResponse.java
Normal file
@@ -0,0 +1,128 @@
|
||||
package com.petshop.backend.dto.refund;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
public class RefundResponse {
|
||||
private Long id;
|
||||
private Long saleId;
|
||||
private Long customerId;
|
||||
private BigDecimal amount;
|
||||
private String reason;
|
||||
private String status;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
public RefundResponse() {
|
||||
}
|
||||
|
||||
public RefundResponse(Long id, Long saleId, Long customerId, BigDecimal amount, String reason, String status, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
this.id = id;
|
||||
this.saleId = saleId;
|
||||
this.customerId = customerId;
|
||||
this.amount = amount;
|
||||
this.reason = reason;
|
||||
this.status = status;
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getSaleId() {
|
||||
return saleId;
|
||||
}
|
||||
|
||||
public void setSaleId(Long saleId) {
|
||||
this.saleId = saleId;
|
||||
}
|
||||
|
||||
public Long getCustomerId() {
|
||||
return customerId;
|
||||
}
|
||||
|
||||
public void setCustomerId(Long customerId) {
|
||||
this.customerId = customerId;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public void setAmount(BigDecimal amount) {
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public String getReason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
public void setReason(String reason) {
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
RefundResponse that = (RefundResponse) o;
|
||||
return Objects.equals(id, that.id) &&
|
||||
Objects.equals(saleId, that.saleId) &&
|
||||
Objects.equals(customerId, that.customerId) &&
|
||||
Objects.equals(amount, that.amount) &&
|
||||
Objects.equals(reason, that.reason) &&
|
||||
Objects.equals(status, that.status) &&
|
||||
Objects.equals(createdAt, that.createdAt) &&
|
||||
Objects.equals(updatedAt, that.updatedAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, saleId, customerId, amount, reason, status, createdAt, updatedAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RefundResponse{" +
|
||||
"id=" + id +
|
||||
", saleId=" + saleId +
|
||||
", customerId=" + customerId +
|
||||
", amount=" + amount +
|
||||
", reason='" + reason + '\'' +
|
||||
", status='" + status + '\'' +
|
||||
", createdAt=" + createdAt +
|
||||
", updatedAt=" + updatedAt +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.petshop.backend.dto.refund;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import java.util.Objects;
|
||||
|
||||
public class RefundUpdateRequest {
|
||||
@NotBlank(message = "Status is required")
|
||||
private String status;
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
RefundUpdateRequest that = (RefundUpdateRequest) o;
|
||||
return Objects.equals(status, that.status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RefundUpdateRequest{" +
|
||||
"status='" + status + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
151
src/main/java/com/petshop/backend/entity/Refund.java
Normal file
151
src/main/java/com/petshop/backend/entity/Refund.java
Normal file
@@ -0,0 +1,151 @@
|
||||
package com.petshop.backend.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
import org.hibernate.annotations.UpdateTimestamp;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
@Entity
|
||||
@Table(name = "refund")
|
||||
public class Refund {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Long saleId;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Long customerId;
|
||||
|
||||
@Column(nullable = false, precision = 10, scale = 2)
|
||||
private BigDecimal amount;
|
||||
|
||||
@Column(nullable = false, length = 500)
|
||||
private String reason;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(nullable = false, length = 20)
|
||||
private RefundStatus status;
|
||||
|
||||
@CreationTimestamp
|
||||
@Column(name = "created_at", updatable = false)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@UpdateTimestamp
|
||||
@Column(name = "updated_at")
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
public enum RefundStatus {
|
||||
PENDING, APPROVED, REJECTED
|
||||
}
|
||||
|
||||
public Refund() {
|
||||
}
|
||||
|
||||
public Refund(Long id, Long saleId, Long customerId, BigDecimal amount, String reason, RefundStatus status, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
this.id = id;
|
||||
this.saleId = saleId;
|
||||
this.customerId = customerId;
|
||||
this.amount = amount;
|
||||
this.reason = reason;
|
||||
this.status = status;
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getSaleId() {
|
||||
return saleId;
|
||||
}
|
||||
|
||||
public void setSaleId(Long saleId) {
|
||||
this.saleId = saleId;
|
||||
}
|
||||
|
||||
public Long getCustomerId() {
|
||||
return customerId;
|
||||
}
|
||||
|
||||
public void setCustomerId(Long customerId) {
|
||||
this.customerId = customerId;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public void setAmount(BigDecimal amount) {
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public String getReason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
public void setReason(String reason) {
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
public RefundStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(RefundStatus 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Refund refund = (Refund) o;
|
||||
return Objects.equals(id, refund.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Refund{" +
|
||||
"id=" + id +
|
||||
", saleId=" + saleId +
|
||||
", customerId=" + customerId +
|
||||
", amount=" + amount +
|
||||
", reason='" + reason + '\'' +
|
||||
", status=" + status +
|
||||
", createdAt=" + createdAt +
|
||||
", updatedAt=" + updatedAt +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,10 @@ public class Sale {
|
||||
@JoinColumn(name = "storeId", nullable = false)
|
||||
private StoreLocation store;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "customerId")
|
||||
private Customer customer;
|
||||
|
||||
@Column(nullable = false, precision = 10, scale = 2)
|
||||
private BigDecimal totalAmount;
|
||||
|
||||
@@ -56,11 +60,12 @@ public class Sale {
|
||||
public Sale() {
|
||||
}
|
||||
|
||||
public Sale(Long saleId, LocalDateTime saleDate, Employee employee, StoreLocation store, BigDecimal totalAmount, String paymentMethod, Boolean isRefund, Sale originalSale, List<SaleItem> items, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
public Sale(Long saleId, LocalDateTime saleDate, Employee employee, StoreLocation store, Customer customer, BigDecimal totalAmount, String paymentMethod, Boolean isRefund, Sale originalSale, List<SaleItem> items, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
this.saleId = saleId;
|
||||
this.saleDate = saleDate;
|
||||
this.employee = employee;
|
||||
this.store = store;
|
||||
this.customer = customer;
|
||||
this.totalAmount = totalAmount;
|
||||
this.paymentMethod = paymentMethod;
|
||||
this.isRefund = isRefund;
|
||||
@@ -102,6 +107,14 @@ public class Sale {
|
||||
this.store = store;
|
||||
}
|
||||
|
||||
public Customer getCustomer() {
|
||||
return customer;
|
||||
}
|
||||
|
||||
public void setCustomer(Customer customer) {
|
||||
this.customer = customer;
|
||||
}
|
||||
|
||||
public BigDecimal getTotalAmount() {
|
||||
return totalAmount;
|
||||
}
|
||||
@@ -178,6 +191,7 @@ public class Sale {
|
||||
", saleDate=" + saleDate +
|
||||
", employee=" + employee +
|
||||
", store=" + store +
|
||||
", customer=" + customer +
|
||||
", totalAmount=" + totalAmount +
|
||||
", paymentMethod='" + paymentMethod + '\'' +
|
||||
", isRefund=" + isRefund +
|
||||
|
||||
@@ -21,6 +21,15 @@ public class User {
|
||||
@Column(nullable = false)
|
||||
private String password;
|
||||
|
||||
@Column(unique = true, length = 100)
|
||||
private String email;
|
||||
|
||||
@Column(length = 100)
|
||||
private String fullName;
|
||||
|
||||
@Column(length = 255)
|
||||
private String avatarUrl;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(nullable = false, length = 20, columnDefinition = "VARCHAR(20)")
|
||||
private Role role;
|
||||
@@ -34,16 +43,19 @@ public class User {
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
public enum Role {
|
||||
STAFF, ADMIN
|
||||
CUSTOMER, STAFF, ADMIN
|
||||
}
|
||||
|
||||
public User() {
|
||||
}
|
||||
|
||||
public User(Long id, String username, String password, Role role, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
public User(Long id, String username, String password, String email, String fullName, String avatarUrl, Role role, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
this.id = id;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.email = email;
|
||||
this.fullName = fullName;
|
||||
this.avatarUrl = avatarUrl;
|
||||
this.role = role;
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
@@ -73,6 +85,30 @@ public class User {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getFullName() {
|
||||
return fullName;
|
||||
}
|
||||
|
||||
public void setFullName(String fullName) {
|
||||
this.fullName = fullName;
|
||||
}
|
||||
|
||||
public String getAvatarUrl() {
|
||||
return avatarUrl;
|
||||
}
|
||||
|
||||
public void setAvatarUrl(String avatarUrl) {
|
||||
this.avatarUrl = avatarUrl;
|
||||
}
|
||||
|
||||
public Role getRole() {
|
||||
return role;
|
||||
}
|
||||
@@ -116,6 +152,9 @@ public class User {
|
||||
"id=" + id +
|
||||
", username='" + username + '\'' +
|
||||
", password='" + password + '\'' +
|
||||
", email='" + email + '\'' +
|
||||
", fullName='" + fullName + '\'' +
|
||||
", avatarUrl='" + avatarUrl + '\'' +
|
||||
", role=" + role +
|
||||
", createdAt=" + createdAt +
|
||||
", updatedAt=" + updatedAt +
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.petshop.backend.repository;
|
||||
|
||||
import com.petshop.backend.entity.Refund;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface RefundRepository extends JpaRepository<Refund, Long> {
|
||||
List<Refund> findByCustomerId(Long customerId);
|
||||
List<Refund> findBySaleId(Long saleId);
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import java.util.Optional;
|
||||
@Repository
|
||||
public interface UserRepository extends JpaRepository<User, Long> {
|
||||
Optional<User> findByUsername(String username);
|
||||
Optional<User> findByEmail(String email);
|
||||
boolean existsByUsername(String username);
|
||||
|
||||
@Query("SELECT u FROM User u WHERE " +
|
||||
|
||||
@@ -36,18 +36,14 @@ public class SecurityConfig {
|
||||
http
|
||||
.csrf(AbstractHttpConfigurer::disable)
|
||||
.authorizeHttpRequests(auth -> auth
|
||||
.requestMatchers("/api/v1/auth/login").permitAll()
|
||||
.requestMatchers("/api/v1/auth/login", "/api/v1/auth/register").permitAll()
|
||||
.requestMatchers("/api/v1/health").permitAll()
|
||||
.requestMatchers("/swagger-ui/**", "/v3/api-docs/**", "/swagger-ui.html").permitAll()
|
||||
.requestMatchers(HttpMethod.GET, "/api/v1/pets/**").permitAll()
|
||||
.requestMatchers(HttpMethod.GET, "/api/v1/products/**").permitAll()
|
||||
.requestMatchers(HttpMethod.GET, "/api/v1/sales/**").permitAll()
|
||||
.requestMatchers(HttpMethod.GET, "/api/v1/dropdowns/suppliers").hasRole("ADMIN")
|
||||
.requestMatchers("/api/v1/inventory/**").hasRole("ADMIN")
|
||||
.requestMatchers("/api/v1/suppliers/**").hasRole("ADMIN")
|
||||
.requestMatchers("/api/v1/product-suppliers/**").hasRole("ADMIN")
|
||||
.requestMatchers("/api/v1/purchase-orders/**").hasRole("ADMIN")
|
||||
.requestMatchers("/api/v1/users/**").hasRole("ADMIN")
|
||||
.requestMatchers("/api/v1/analytics/**").hasRole("ADMIN")
|
||||
.requestMatchers(HttpMethod.GET, "/api/v1/services/**").permitAll()
|
||||
.requestMatchers(HttpMethod.GET, "/api/v1/categories/**").permitAll()
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||
|
||||
111
src/main/java/com/petshop/backend/service/RefundService.java
Normal file
111
src/main/java/com/petshop/backend/service/RefundService.java
Normal file
@@ -0,0 +1,111 @@
|
||||
package com.petshop.backend.service;
|
||||
|
||||
import com.petshop.backend.dto.refund.RefundRequest;
|
||||
import com.petshop.backend.dto.refund.RefundResponse;
|
||||
import com.petshop.backend.entity.Refund;
|
||||
import com.petshop.backend.entity.Sale;
|
||||
import com.petshop.backend.entity.User;
|
||||
import com.petshop.backend.repository.RefundRepository;
|
||||
import com.petshop.backend.repository.SaleRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class RefundService {
|
||||
|
||||
private final RefundRepository refundRepository;
|
||||
private final SaleRepository saleRepository;
|
||||
|
||||
public RefundService(RefundRepository refundRepository, SaleRepository saleRepository) {
|
||||
this.refundRepository = refundRepository;
|
||||
this.saleRepository = saleRepository;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public RefundResponse createRefund(RefundRequest request, Long customerId) {
|
||||
Sale sale = saleRepository.findById(request.getSaleId())
|
||||
.orElseThrow(() -> new RuntimeException("Sale not found"));
|
||||
|
||||
if (sale.getCustomer() == null) {
|
||||
throw new RuntimeException("Sale has no associated customer");
|
||||
}
|
||||
|
||||
if (customerId != null && !sale.getCustomer().getCustomerId().equals(customerId)) {
|
||||
throw new RuntimeException("You can only create refunds for your own purchases");
|
||||
}
|
||||
|
||||
Refund refund = new Refund();
|
||||
refund.setSaleId(sale.getSaleId());
|
||||
refund.setCustomerId(sale.getCustomer().getCustomerId());
|
||||
refund.setAmount(sale.getTotalAmount());
|
||||
refund.setReason(request.getReason());
|
||||
refund.setStatus(Refund.RefundStatus.PENDING);
|
||||
|
||||
Refund savedRefund = refundRepository.save(refund);
|
||||
return toResponse(savedRefund);
|
||||
}
|
||||
|
||||
public RefundResponse getRefundById(Long id, Long customerId) {
|
||||
Refund refund = refundRepository.findById(id)
|
||||
.orElseThrow(() -> new RuntimeException("Refund not found"));
|
||||
|
||||
if (customerId != null && !refund.getCustomerId().equals(customerId)) {
|
||||
throw new RuntimeException("You can only view your own refunds");
|
||||
}
|
||||
|
||||
return toResponse(refund);
|
||||
}
|
||||
|
||||
public List<RefundResponse> getAllRefunds(Long customerId) {
|
||||
List<Refund> refunds;
|
||||
|
||||
if (customerId != null) {
|
||||
refunds = refundRepository.findByCustomerId(customerId);
|
||||
} else {
|
||||
refunds = refundRepository.findAll();
|
||||
}
|
||||
|
||||
return refunds.stream()
|
||||
.map(this::toResponse)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public RefundResponse updateRefundStatus(Long id, String status) {
|
||||
Refund refund = refundRepository.findById(id)
|
||||
.orElseThrow(() -> new RuntimeException("Refund not found"));
|
||||
|
||||
try {
|
||||
refund.setStatus(Refund.RefundStatus.valueOf(status.toUpperCase()));
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new RuntimeException("Invalid status: " + status);
|
||||
}
|
||||
|
||||
Refund updatedRefund = refundRepository.save(refund);
|
||||
return toResponse(updatedRefund);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void deleteRefund(Long id) {
|
||||
if (!refundRepository.existsById(id)) {
|
||||
throw new RuntimeException("Refund not found");
|
||||
}
|
||||
refundRepository.deleteById(id);
|
||||
}
|
||||
|
||||
private RefundResponse toResponse(Refund refund) {
|
||||
return new RefundResponse(
|
||||
refund.getId(),
|
||||
refund.getSaleId(),
|
||||
refund.getCustomerId(),
|
||||
refund.getAmount(),
|
||||
refund.getReason(),
|
||||
refund.getStatus().name(),
|
||||
refund.getCreatedAt(),
|
||||
refund.getUpdatedAt()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,12 @@ spring:
|
||||
application:
|
||||
name: petshop-backend
|
||||
|
||||
servlet:
|
||||
multipart:
|
||||
enabled: true
|
||||
max-file-size: 5MB
|
||||
max-request-size: 5MB
|
||||
|
||||
datasource:
|
||||
url: ${SPRING_DATASOURCE_URL:jdbc:mysql://localhost:3306/Petstoredb?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC}
|
||||
username: ${SPRING_DATASOURCE_USERNAME:petshop}
|
||||
|
||||
@@ -152,12 +152,14 @@ CREATE TABLE IF NOT EXISTS sale (
|
||||
paymentMethod VARCHAR(50) NOT NULL,
|
||||
employeeId BIGINT NOT NULL,
|
||||
storeId BIGINT NOT NULL,
|
||||
customerId BIGINT NULL,
|
||||
isRefund BOOLEAN DEFAULT FALSE NOT NULL,
|
||||
originalSaleId BIGINT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (employeeId) REFERENCES employee(employeeId),
|
||||
FOREIGN KEY (storeId) REFERENCES storeLocation(storeId),
|
||||
FOREIGN KEY (customerId) REFERENCES customer(customerId),
|
||||
FOREIGN KEY (originalSaleId) REFERENCES sale(saleId)
|
||||
);
|
||||
|
||||
@@ -195,7 +197,45 @@ CREATE TABLE IF NOT EXISTS users (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
username VARCHAR(50) UNIQUE NOT NULL,
|
||||
password VARCHAR(255) NOT NULL,
|
||||
email VARCHAR(100) UNIQUE,
|
||||
fullName VARCHAR(100),
|
||||
avatarUrl VARCHAR(255),
|
||||
role VARCHAR(20) NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS refund (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
saleId BIGINT NOT NULL,
|
||||
customerId BIGINT NOT NULL,
|
||||
amount DECIMAL(10, 2) NOT NULL,
|
||||
reason VARCHAR(500) NOT NULL,
|
||||
status VARCHAR(20) NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (saleId) REFERENCES sale(saleId),
|
||||
FOREIGN KEY (customerId) REFERENCES customer(customerId)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS conversation (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
customerId BIGINT NOT NULL,
|
||||
staffId BIGINT,
|
||||
status VARCHAR(20) DEFAULT 'OPEN',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (customerId) REFERENCES customer(customerId),
|
||||
FOREIGN KEY (staffId) REFERENCES users(id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS message (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
conversationId BIGINT NOT NULL,
|
||||
senderId BIGINT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
isRead BOOLEAN DEFAULT FALSE,
|
||||
FOREIGN KEY (conversationId) REFERENCES conversation(id),
|
||||
FOREIGN KEY (senderId) REFERENCES users(id)
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user