├── system.properties ├── Procfile ├── .vs ├── ProjectSettings.json ├── slnx.sqlite ├── VSWorkspaceState.json └── AuctionWebApp_BE │ ├── v17 │ ├── .wsuo │ └── DocumentLayout.json │ └── FileContentIndex │ ├── 8e1dd749-345b-4c10-8543-cb8ea87a4afd.vsidx │ └── bfd44eb4-5f5d-4564-882e-c3536e2f6827.vsidx ├── logo.png ├── get_version.sh ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── src ├── main │ ├── java │ │ └── vn │ │ │ └── webapp │ │ │ └── backend │ │ │ └── auction │ │ │ ├── enums │ │ │ ├── TokenType.java │ │ │ ├── Role.java │ │ │ ├── AccountState.java │ │ │ ├── PaymentMethod.java │ │ │ ├── AuctionHistoryState.java │ │ │ ├── RequestApprovalState.java │ │ │ ├── JewelryMaterial.java │ │ │ ├── AuctionRegistrationState.java │ │ │ ├── TransactionState.java │ │ │ ├── AuctionState.java │ │ │ ├── JewelryState.java │ │ │ └── TransactionType.java │ │ │ ├── dto │ │ │ ├── ActivateAccountRequest.java │ │ │ ├── ForgotPasswordRequest.java │ │ │ ├── RotateRefreshToken.java │ │ │ ├── KickOutMessage.java │ │ │ ├── ResetPasswordRequest.java │ │ │ ├── CancelRequestApproval.java │ │ │ ├── BidMessage.java │ │ │ ├── ImageRequest.java │ │ │ ├── AuthenticationRequest.java │ │ │ ├── ChangePasswordRequest.java │ │ │ ├── DisableUserRequest.java │ │ │ ├── UserRequestApproval.java │ │ │ ├── BidRequest.java │ │ │ ├── KickOutResponse.java │ │ │ ├── ManagerRequestApproval.java │ │ │ ├── StaffRequestApproval.java │ │ │ ├── UserSpentResponse.java │ │ │ ├── PaymentResponse.java │ │ │ ├── BidResponse.java │ │ │ ├── JewelryCreateRequest.java │ │ │ ├── AuctionRegistrationResponse.java │ │ │ ├── SendJewelryFromUserRequest.java │ │ │ ├── AuctionRequest.java │ │ │ ├── DashBoardRequest.java │ │ │ ├── UserTransactionResponse.java │ │ │ ├── AuthenticationResponse.java │ │ │ ├── JewelryUpdateRequest.java │ │ │ ├── RegisterAccountRequest.java │ │ │ └── DashBoardResponse.java │ │ │ ├── model │ │ │ ├── ReasonMessages.java │ │ │ ├── ErrorResponse.java │ │ │ ├── JewelryCategory.java │ │ │ ├── Bank.java │ │ │ ├── Image.java │ │ │ ├── Token.java │ │ │ ├── ErrorMessages.java │ │ │ ├── AuctionHistory.java │ │ │ ├── AuctionRegistration.java │ │ │ ├── RequestApproval.java │ │ │ ├── Transaction.java │ │ │ ├── Auction.java │ │ │ ├── Jewelry.java │ │ │ └── User.java │ │ │ ├── exception │ │ │ ├── UnauthorizedException.java │ │ │ ├── UserNotAllowedAccess.java │ │ │ ├── ExpiredTokenException.java │ │ │ ├── UserNotFoundException.java │ │ │ ├── AccountDisabledException.java │ │ │ ├── AccountInactiveException.java │ │ │ ├── ResourceNotFoundException.java │ │ │ ├── UserAlreadyExistsException.java │ │ │ ├── OldPasswordMismatchException.java │ │ │ └── GlobalExceptionHandler.java │ │ │ ├── service │ │ │ ├── role │ │ │ │ ├── RoleService.java │ │ │ │ └── RoleServiceImpl.java │ │ │ ├── bank │ │ │ │ ├── BankService.java │ │ │ │ └── BankServiceImpl.java │ │ │ ├── category │ │ │ │ ├── JewelryCategoryService.java │ │ │ │ └── JewelryCategoryServiceImpl.java │ │ │ ├── image │ │ │ │ ├── ImageService.java │ │ │ │ └── ImageServiceImpl.java │ │ │ ├── auction_registration │ │ │ │ ├── AuctionRegistrationService.java │ │ │ │ └── AuctionRegistrationServiceImpl.java │ │ │ ├── vnpay │ │ │ │ ├── ResponseObject.java │ │ │ │ └── VNPayUtil.java │ │ │ ├── bid │ │ │ │ └── AuctionHistoryService.java │ │ │ ├── authenticate │ │ │ │ └── AuthenticationService.java │ │ │ ├── request_approval │ │ │ │ └── RequestApprovalService.java │ │ │ ├── user │ │ │ │ └── UserService.java │ │ │ ├── auction │ │ │ │ └── AuctionService.java │ │ │ ├── transaction │ │ │ │ └── TransactionService.java │ │ │ ├── logout │ │ │ │ └── LogoutService.java │ │ │ ├── jewelry │ │ │ │ └── JewelryService.java │ │ │ ├── payment │ │ │ │ └── PaymentService.java │ │ │ ├── realtime │ │ │ │ └── RealTimeService.java │ │ │ ├── email │ │ │ │ └── EmailService.java │ │ │ └── jwt │ │ │ │ └── JwtService.java │ │ │ ├── repository │ │ │ ├── BankRepository.java │ │ │ ├── JewelryCategoryRepository.java │ │ │ ├── TokenRepository.java │ │ │ ├── ImageRepository.java │ │ │ ├── AuctionHistoryRepository.java │ │ │ ├── AuctionRegistrationRepository.java │ │ │ ├── RequestApprovalRepository.java │ │ │ ├── AuctionRepository.java │ │ │ ├── UserRepository.java │ │ │ └── JewelryRepository.java │ │ │ ├── config │ │ │ ├── frontend │ │ │ │ └── FrontendConfiguration.java │ │ │ ├── cors │ │ │ │ └── CorsConfiguration.java │ │ │ ├── webSocket │ │ │ │ └── WebSocketConfiguration.java │ │ │ ├── application │ │ │ │ └── ApplicationConfiguration.java │ │ │ ├── vnpay │ │ │ │ └── VNPAYConfiguration.java │ │ │ └── security │ │ │ │ └── SecurityConfiguration.java │ │ │ ├── AuctionApplication.java │ │ │ ├── controller │ │ │ ├── DashBoardController.java │ │ │ ├── BankController.java │ │ │ ├── ImageController.java │ │ │ ├── JewelryCategoryController.java │ │ │ ├── RealTimeController.java │ │ │ ├── AuctionRegistrationController.java │ │ │ ├── VNPAYController.java │ │ │ ├── AuthenticationController.java │ │ │ └── AuctionHistoryController.java │ │ │ ├── security │ │ │ └── Endpoints.java │ │ │ └── filter │ │ │ └── JwtAuthenticationFilter.java │ └── resources │ │ ├── banner.txt │ │ ├── application-test.properties │ │ ├── application-dev.properties │ │ └── application.properties └── test │ ├── java │ └── vn │ │ └── webapp │ │ └── backend │ │ └── auction │ │ ├── AuctionRegistrationServiceTest.java │ │ ├── BankServiceTest.java │ │ ├── RequestApprovalServiceTest.java │ │ ├── AuctionHistoryServiceTest.java │ │ ├── ImageServiceTest.java │ │ ├── UserServiceTest.java │ │ ├── JewelryCategoryServiceTest.java │ │ ├── TransactionServiceTest.java │ │ ├── JewelryServiceTest.java │ │ └── AuctionServiceTest.java │ └── resources │ └── application.properties ├── Dockerfile ├── docker-compose.yml ├── db └── Dockerfile ├── .gitignore ├── .github └── workflows │ ├── build.yml │ ├── release.yml │ ├── deploy.yml │ └── docker-publish.yml ├── LICENSE ├── README.md └── pom.xml /system.properties: -------------------------------------------------------------------------------- 1 | java.runtime.version=17 -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: java -Dserver.port=$PORT -jar target/*.jar -------------------------------------------------------------------------------- /.vs/ProjectSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "CurrentProjectSetting": null 3 | } -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phuuthanh-dev/jewelry-auction-be/HEAD/logo.png -------------------------------------------------------------------------------- /.vs/slnx.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phuuthanh-dev/jewelry-auction-be/HEAD/.vs/slnx.sqlite -------------------------------------------------------------------------------- /.vs/VSWorkspaceState.json: -------------------------------------------------------------------------------- 1 | { 2 | "ExpandedNodes": [ 3 | "" 4 | ], 5 | "PreviewInSolutionExplorer": false 6 | } -------------------------------------------------------------------------------- /get_version.sh: -------------------------------------------------------------------------------- 1 | # Define the fixed version 2 | version="0.0.5" 3 | 4 | # Output the fixed version 5 | echo "$version" -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phuuthanh-dev/jewelry-auction-be/HEAD/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.vs/AuctionWebApp_BE/v17/.wsuo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phuuthanh-dev/jewelry-auction-be/HEAD/.vs/AuctionWebApp_BE/v17/.wsuo -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/enums/TokenType.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.enums; 2 | 3 | public enum TokenType { 4 | BEARER 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/enums/Role.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.enums; 2 | 3 | public enum Role { 4 | MEMBER, STAFF, MANAGER, ADMIN 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/dto/ActivateAccountRequest.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.dto; 2 | 3 | public record ActivateAccountRequest (String token){ 4 | } 5 | -------------------------------------------------------------------------------- /.vs/AuctionWebApp_BE/FileContentIndex/8e1dd749-345b-4c10-8543-cb8ea87a4afd.vsidx: -------------------------------------------------------------------------------- 1 | CDGG '3 -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/dto/ForgotPasswordRequest.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.dto; 2 | 3 | public record ForgotPasswordRequest( 4 | String email) { 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/dto/RotateRefreshToken.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.dto; 2 | 3 | public record RotateRefreshToken ( 4 | String refreshToken 5 | ) { 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/dto/KickOutMessage.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.dto; 2 | 3 | public record KickOutMessage ( 4 | Integer auctionId, 5 | String username 6 | ){ 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/enums/AccountState.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.enums; 2 | 3 | public enum AccountState { 4 | VERIFIED, ACTIVE, INACTIVE, DISABLE, BAN_PARTICIPATING 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/dto/ResetPasswordRequest.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.dto; 2 | 3 | public record ResetPasswordRequest( 4 | String token, 5 | String password) { 6 | } 7 | -------------------------------------------------------------------------------- /.vs/AuctionWebApp_BE/FileContentIndex/bfd44eb4-5f5d-4564-882e-c3536e2f6827.vsidx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phuuthanh-dev/jewelry-auction-be/HEAD/.vs/AuctionWebApp_BE/FileContentIndex/bfd44eb4-5f5d-4564-882e-c3536e2f6827.vsidx -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/dto/CancelRequestApproval.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.dto; 2 | 3 | public record CancelRequestApproval( 4 | Integer requestId, 5 | String note 6 | ) { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/dto/BidMessage.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.dto; 2 | 3 | public record BidMessage( 4 | String username, 5 | Integer auctionId, 6 | Long bonusTime 7 | ) { 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/dto/ImageRequest.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.dto; 2 | 3 | public record ImageRequest( 4 | boolean icon, 5 | String data, 6 | Integer jewelryId 7 | ) { 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/dto/AuthenticationRequest.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.dto; 2 | 3 | public record AuthenticationRequest ( 4 | String username, 5 | String email, 6 | String password 7 | ) { 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/dto/ChangePasswordRequest.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.dto; 2 | 3 | public record ChangePasswordRequest( 4 | String token, 5 | String oldPassword, 6 | String newPassword) { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/model/ReasonMessages.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.model; 2 | 3 | public class ReasonMessages { 4 | public static final String DO_NOT_PAY_ON_TIME = "Không thanh toán hóa đơn đúng thời hạn quy định."; 5 | } 6 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar 3 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/exception/UnauthorizedException.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.exception; 2 | 3 | public class UnauthorizedException extends RuntimeException { 4 | public UnauthorizedException(String message) { 5 | super(message); 6 | } 7 | } -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/exception/UserNotAllowedAccess.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.exception; 2 | 3 | public class UserNotAllowedAccess extends RuntimeException { 4 | public UserNotAllowedAccess(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM maven:3.8.4-openjdk-17-slim AS build 2 | WORKDIR /app 3 | COPY . . 4 | RUN mvn clean package -DskipTests 5 | 6 | FROM openjdk:17-jdk-alpine 7 | WORKDIR /app 8 | COPY --from=build /app/target/*.jar /app/app.jar 9 | EXPOSE 8080 10 | CMD ["java", "-jar", "/app/app.jar"] 11 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/dto/DisableUserRequest.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.dto; 2 | 3 | import vn.webapp.backend.auction.enums.AccountState; 4 | 5 | public record DisableUserRequest( 6 | AccountState state, 7 | String reason 8 | ) { 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/exception/ExpiredTokenException.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.exception; 2 | 3 | public class ExpiredTokenException extends RuntimeException { 4 | public ExpiredTokenException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/service/role/RoleService.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.service.role; 2 | 3 | import vn.webapp.backend.auction.enums.Role; 4 | 5 | import java.util.List; 6 | 7 | public interface RoleService { 8 | List getAllRoles(); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/exception/UserNotFoundException.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.exception; 2 | 3 | public class UserNotFoundException extends RuntimeException { 4 | public UserNotFoundException(String message) { 5 | super(message); 6 | } 7 | } 8 | 9 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/exception/AccountDisabledException.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.exception; 2 | 3 | public class AccountDisabledException extends RuntimeException { 4 | public AccountDisabledException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/exception/AccountInactiveException.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.exception; 2 | 3 | public class AccountInactiveException extends RuntimeException { 4 | public AccountInactiveException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/exception/ResourceNotFoundException.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.exception; 2 | 3 | public class ResourceNotFoundException extends RuntimeException { 4 | public ResourceNotFoundException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/dto/UserRequestApproval.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.dto; 2 | 3 | import java.sql.Timestamp; 4 | 5 | public record UserRequestApproval( 6 | Integer senderId, 7 | Integer jewelryId, 8 | Timestamp requestTime 9 | ) { 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/exception/UserAlreadyExistsException.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.exception; 2 | 3 | public class UserAlreadyExistsException extends RuntimeException { 4 | public UserAlreadyExistsException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/dto/BidRequest.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.dto; 2 | 3 | import java.sql.Timestamp; 4 | 5 | public record BidRequest( 6 | String username, 7 | Integer auctionId, 8 | Double priceGiven, 9 | Timestamp bidTime 10 | ) { 11 | } -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/exception/OldPasswordMismatchException.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.exception; 2 | 3 | public class OldPasswordMismatchException extends RuntimeException { 4 | public OldPasswordMismatchException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/service/bank/BankService.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.service.bank; 2 | 3 | import vn.webapp.backend.auction.model.Bank; 4 | 5 | import java.util.List; 6 | public interface BankService { 7 | List getAll(); 8 | Bank getById(Integer id); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/dto/KickOutResponse.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.dto; 2 | 3 | import java.sql.Timestamp; 4 | 5 | public record KickOutResponse ( 6 | Integer userId, 7 | Double lastPrice, 8 | String kickReason, 9 | Timestamp endDate 10 | ){ 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/dto/ManagerRequestApproval.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.dto; 2 | 3 | import java.sql.Timestamp; 4 | 5 | public record ManagerRequestApproval( 6 | Integer senderId, 7 | Integer requestApprovalId, 8 | Timestamp requestTime 9 | ) { 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/repository/BankRepository.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.repository; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import vn.webapp.backend.auction.model.Bank; 5 | 6 | public interface BankRepository extends JpaRepository { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/dto/StaffRequestApproval.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.dto; 2 | 3 | import java.sql.Timestamp; 4 | 5 | public record StaffRequestApproval( 6 | Integer senderId, 7 | Integer requestApprovalId, 8 | Double valuation, 9 | Timestamp requestTime 10 | ) { 11 | } 12 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.0' 2 | 3 | services: 4 | auction-database: 5 | image: ghcr.io/phuuthanh-dev/auction-database:latest 6 | ports: 7 | - "3306:3306" 8 | 9 | auction-api: 10 | image: ghcr.io/phuuthanh-dev/auction-api:latest 11 | ports: 12 | - "8080:8080" 13 | depends_on: 14 | - auction-database 15 | -------------------------------------------------------------------------------- /.vs/AuctionWebApp_BE/v17/DocumentLayout.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": 1, 3 | "WorkspaceRootPath": "D:\\Study\\CN5\\SWP\\software-development-project-main\\AuctionWebApp_BE\\", 4 | "Documents": [], 5 | "DocumentGroupContainers": [ 6 | { 7 | "Orientation": 0, 8 | "VerticalTabListWidth": 256, 9 | "DocumentGroups": [] 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/dto/UserSpentResponse.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.dto; 2 | 3 | public record UserSpentResponse ( 4 | Integer id, 5 | String avatar, 6 | String username, 7 | String fullName, 8 | String email, 9 | String phone, 10 | Double totalSpent 11 | ) { 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/dto/PaymentResponse.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.dto; 2 | 3 | import lombok.Builder; 4 | 5 | public abstract class PaymentResponse { 6 | @Builder 7 | public static class VNPayResponse { 8 | public String code; 9 | public String message; 10 | public String paymentUrl; 11 | } 12 | } -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/dto/BidResponse.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.dto; 2 | 3 | import java.sql.Timestamp; 4 | 5 | public record BidResponse ( 6 | Double lastPrice, 7 | Double buyNowPrice, 8 | Integer auctionId, 9 | Timestamp endDate, 10 | Long bonusTime, 11 | String username 12 | ) { 13 | } 14 | -------------------------------------------------------------------------------- /db/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use the official MySQL image 2 | FROM mysql:8.2.0 3 | 4 | # Set environment variables for MySQL 5 | ENV MYSQL_ALLOW_EMPTY_PASSWORD yes 6 | ENV MYSQL_DATABASE=DB_AUCTION 7 | ENV MYSQL_COLLATION=utf8mb4_general_ci 8 | 9 | # Copy the SQL script into the container 10 | COPY init.sql /docker-entrypoint-initdb.d/init.sql 11 | 12 | # Expose MySQL port 13 | EXPOSE 3306 14 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/dto/JewelryCreateRequest.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.dto; 2 | 3 | public record JewelryCreateRequest( 4 | String username, 5 | String name, 6 | String token, 7 | String description, 8 | String category, 9 | Double buyNowPrice, 10 | String material, 11 | String brand, 12 | Double weight 13 | ) { 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/enums/PaymentMethod.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.enums; 2 | 3 | import lombok.Getter; 4 | 5 | @Getter 6 | public enum PaymentMethod { 7 | PAY_AT_COUNTER("Thanh toán tại quầy"), 8 | BANKING("Chuyển khoản"); 9 | 10 | private String displayName; 11 | 12 | PaymentMethod(String displayName) { 13 | this.displayName = displayName; 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/repository/JewelryCategoryRepository.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.repository; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import vn.webapp.backend.auction.model.JewelryCategory; 5 | 6 | import java.util.Optional; 7 | 8 | public interface JewelryCategoryRepository extends JpaRepository { 9 | Optional findByName(String name); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/dto/AuctionRegistrationResponse.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.dto; 2 | 3 | import vn.webapp.backend.auction.enums.AuctionState; 4 | 5 | import java.sql.Timestamp; 6 | 7 | public record AuctionRegistrationResponse( 8 | Integer id, 9 | String name, 10 | Timestamp startDate, 11 | Timestamp endDate, 12 | AuctionState state, 13 | Integer numberOfParticipants 14 | ) { 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/config/frontend/FrontendConfiguration.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.config.frontend; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | @Configuration 8 | @ConfigurationProperties(prefix = "frontend") 9 | @Data 10 | public class FrontendConfiguration { 11 | private String baseUrl; 12 | } 13 | 14 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/enums/AuctionHistoryState.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.enums; 2 | 3 | public enum AuctionHistoryState { 4 | ACTIVE("Đang hiển thị"), 5 | HIDDEN("Đã bị ẩn"); 6 | 7 | private String vietnameseName; 8 | 9 | AuctionHistoryState(String vietnameseName) { 10 | this.vietnameseName = vietnameseName; 11 | } 12 | 13 | public String getVietnameseName() { 14 | return vietnameseName; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/enums/RequestApprovalState.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.enums; 2 | 3 | public enum RequestApprovalState { 4 | ACTIVE("Đang hiển thị"), 5 | HIDDEN("Đã bị ẩn"); 6 | 7 | private String vietnameseName; 8 | 9 | RequestApprovalState(String vietnameseName) { 10 | this.vietnameseName = vietnameseName; 11 | } 12 | 13 | public String getVietnameseName() { 14 | return vietnameseName; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/dto/SendJewelryFromUserRequest.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.dto; 2 | 3 | import vn.webapp.backend.auction.enums.JewelryMaterial; 4 | 5 | public record SendJewelryFromUserRequest( 6 | Integer userId, 7 | Double buyNowPrice, 8 | String description, 9 | JewelryMaterial material, 10 | Double weight, 11 | String brand, 12 | String category, 13 | String name 14 | 15 | ) { 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/enums/JewelryMaterial.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.enums; 2 | 3 | public enum JewelryMaterial { 4 | GOLD("Vàng"), 5 | SILVER("Bạc"), 6 | PLATINUM("Bạch kim"), 7 | DIAMOND("Kim cương"); 8 | 9 | private String displayName; 10 | 11 | JewelryMaterial(String displayName) { 12 | this.displayName = displayName; 13 | } 14 | 15 | public String getDisplayName() { 16 | return displayName; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/service/category/JewelryCategoryService.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.service.category; 2 | 3 | import vn.webapp.backend.auction.model.JewelryCategory; 4 | 5 | import java.util.List; 6 | 7 | public interface JewelryCategoryService { 8 | List getAll(); 9 | JewelryCategory getById(int id); 10 | JewelryCategory saveJewelryCategory(JewelryCategory jewelryCategory); 11 | void deleteJewelryCategory(Integer id); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/dto/AuctionRequest.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.dto; 2 | 3 | import java.sql.Timestamp; 4 | 5 | public record AuctionRequest( 6 | String name, 7 | Timestamp startDate, 8 | Timestamp endDate, 9 | String description, 10 | Double firstPrice, 11 | Double participationFee, 12 | Double deposit, 13 | Double priceStep, 14 | Integer jewelryId, 15 | Integer staffId 16 | ) { 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/dto/DashBoardRequest.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.dto; 2 | 3 | 4 | public record DashBoardRequest ( 5 | Integer yearGetRegisterAccount, 6 | Integer yearGetAuction, 7 | Integer yearGetRevenue, 8 | Integer yearGetAuctionFailedAndSuccess, 9 | Integer monthGetAuctionFailedAndSuccess, 10 | Integer yearGetJewelry, 11 | Integer monthGetJewelry, 12 | Integer yearGetUserJoinAuction 13 | ) { 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/service/image/ImageService.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.service.image; 2 | 3 | import vn.webapp.backend.auction.dto.ImageRequest; 4 | import vn.webapp.backend.auction.model.Image; 5 | 6 | import java.util.List; 7 | 8 | public interface ImageService { 9 | List getImagesByJewelryId(Integer id); 10 | Image getImageByIconAndJewelryId(Integer id); 11 | Image createImage(ImageRequest request); 12 | void deleteByJewelryId(Integer id); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/dto/UserTransactionResponse.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | @Data 8 | @Builder 9 | @AllArgsConstructor 10 | @NoArgsConstructor 11 | public class UserTransactionResponse { 12 | Double totalPriceJewelryWonByUsername; 13 | Integer numberTransactionsRequest; 14 | Integer totalJewelryWon; 15 | Integer totalBid; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/AuctionApplication.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.scheduling.annotation.EnableAsync; 6 | 7 | @SpringBootApplication 8 | @EnableAsync 9 | public class AuctionApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(AuctionApplication.class, args); 13 | } 14 | 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/enums/AuctionRegistrationState.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.enums; 2 | 3 | public enum AuctionRegistrationState { 4 | VALID("Được phép tham gia"), 5 | KICKED_OUT("Bị loại khỏi đấu giá"); 6 | 7 | private String vietnameseName; 8 | 9 | AuctionRegistrationState(String vietnameseName) { 10 | this.vietnameseName = vietnameseName; 11 | } 12 | 13 | public String getVietnameseName() { 14 | return vietnameseName; 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/dto/AuthenticationResponse.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | @Data 10 | @Builder 11 | @AllArgsConstructor 12 | @NoArgsConstructor 13 | public class AuthenticationResponse { 14 | 15 | @JsonProperty("access_token") 16 | private String accessToken; 17 | 18 | private String banReason; 19 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/enums/TransactionState.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.enums; 2 | 3 | public enum TransactionState { 4 | SUCCEED("Thanh toán thành công"), 5 | PENDING("Chưa thanh toán..."), 6 | FAILED("Hủy thanh toán"), 7 | HIDDEN("Ẩn"); 8 | 9 | 10 | private String vietnameseName; 11 | 12 | TransactionState(String vietnameseName) { 13 | this.vietnameseName = vietnameseName; 14 | } 15 | 16 | public String getVietnameseName() { 17 | return vietnameseName; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/enums/AuctionState.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.enums; 2 | 3 | public enum AuctionState { 4 | 5 | ONGOING("Đang diễn ra"), 6 | WAITING("Đang chờ"), 7 | FINISHED("Đã kết thúc"), 8 | PAUSED("Tạm dừng"), 9 | DELETED("Đã xóa"); 10 | 11 | private String vietnameseName; 12 | 13 | AuctionState(String vietnameseName) { 14 | this.vietnameseName = vietnameseName; 15 | } 16 | 17 | public String getVietnameseName() { 18 | return vietnameseName; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/enums/JewelryState.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.enums; 2 | 3 | public enum JewelryState { 4 | ACTIVE("Đã duyệt"), 5 | APPROVING("Đang chờ duyệt"), 6 | HIDDEN("Đã bị ẩn"), 7 | AUCTION("Có phiên đấu"), 8 | HANDED_OVER("Đã bàn giao"), 9 | RETURNED("Đã hoàn trả"); 10 | 11 | private String displayName; 12 | 13 | JewelryState(String displayName) { 14 | this.displayName = displayName; 15 | } 16 | 17 | public String getDisplayName() { 18 | return displayName; 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/dto/JewelryUpdateRequest.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.dto; 2 | 3 | import vn.webapp.backend.auction.enums.JewelryMaterial; 4 | 5 | import java.sql.Timestamp; 6 | 7 | public record JewelryUpdateRequest( 8 | Integer id, 9 | String name, 10 | String description, 11 | String category, 12 | Double buyNowPrice, 13 | JewelryMaterial material, 14 | String brand, 15 | Double weight, 16 | Timestamp createDate, 17 | String image, 18 | String state 19 | ) { 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/service/role/RoleServiceImpl.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.service.role; 2 | 3 | import jakarta.transaction.Transactional; 4 | import lombok.RequiredArgsConstructor; 5 | import org.springframework.stereotype.Service; 6 | import vn.webapp.backend.auction.enums.Role; 7 | 8 | import java.util.List; 9 | 10 | @Transactional 11 | @RequiredArgsConstructor 12 | @Service 13 | public class RoleServiceImpl implements RoleService{ 14 | @Override 15 | public List getAllRoles() { 16 | return List.of(Role.values()); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/enums/TransactionType.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.enums; 2 | 3 | public enum TransactionType { 4 | REGISTRATION("Đăng kí tham gia phiên"), 5 | REFUND("Hoàn tiền đăng kí tham gia phiên"), 6 | PAYMENT_TO_SELLER("Thanh toán cho người gửi sản phẩm đấu giá"), 7 | PAYMENT_TO_WINNER("Thanh toán cho người thắng đấu giá"); 8 | 9 | private String vietnameseName; 10 | 11 | TransactionType(String vietnameseName) { 12 | this.vietnameseName = vietnameseName; 13 | } 14 | 15 | public String getVietnameseName() { 16 | return vietnameseName; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/model/ErrorResponse.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @NoArgsConstructor 10 | @AllArgsConstructor 11 | @Builder 12 | public class ErrorResponse { 13 | private int status; 14 | private String message; 15 | private long timestamp; 16 | 17 | public ErrorResponse(int status, String message) { 18 | this.status = status; 19 | this.message = message; 20 | this.timestamp = System.currentTimeMillis(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/service/auction_registration/AuctionRegistrationService.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.service.auction_registration; 2 | 3 | import org.springframework.data.domain.Page; 4 | import org.springframework.data.domain.Pageable; 5 | import vn.webapp.backend.auction.model.AuctionRegistration; 6 | 7 | import java.util.List; 8 | 9 | public interface AuctionRegistrationService { 10 | void registerUserForAuction(String username, Integer auctionId, String transactionCode, String bankCode); 11 | List findByAuctionIdAndValid(Integer auctionId); 12 | Page findByUserIdAndValid(Integer userId, String auctionName, Pageable pageable); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/dto/RegisterAccountRequest.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.dto; 2 | 3 | import vn.webapp.backend.auction.enums.Role; 4 | 5 | public record RegisterAccountRequest( 6 | String firstName, 7 | String lastName, 8 | String username, 9 | String password, 10 | String email, 11 | String address, 12 | String city, 13 | String district, 14 | String ward, 15 | String yob, 16 | String phone, 17 | String cccd, 18 | String cccdFirst, 19 | String cccdLast, 20 | String cccdFrom, 21 | Role role, 22 | String bankAccountName, 23 | String bankAccountNumber, 24 | Integer bankId) { 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/service/vnpay/ResponseObject.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.service.vnpay; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import lombok.Builder; 5 | import org.springframework.http.HttpStatusCode; 6 | import org.springframework.http.ResponseEntity; 7 | public class ResponseObject extends ResponseEntity> { 8 | public ResponseObject(HttpStatusCode code, String message, T data) { 9 | super(new Payload<>(code.value(), message, data),code); 10 | } 11 | @Builder 12 | public static class Payload { 13 | public int code; 14 | public String message; 15 | @JsonInclude(JsonInclude.Include.NON_NULL) 16 | public T data; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | 2 | ${AnsiColor.BRIGHT_CYAN} █████╗ ██╗ ██╗ ██████╗████████╗██╗ ██████╗ ███╗ ██╗ █████╗ ██████╗ ██╗ 3 | ██╔══██╗██║ ██║██╔════╝╚══██╔══╝██║██╔═══██╗████╗ ██║ ██╔══██╗██╔══██╗██║ 4 | ███████║██║ ██║██║ ██║ ██║██║ ██║██╔██╗ ██║ ███████║██████╔╝██║ 5 | ██╔══██║██║ ██║██║ ██║ ██║██║ ██║██║╚██╗██║ ██╔══██║██╔═══╝ ██║ 6 | ██║ ██║╚██████╔╝╚██████╗ ██║ ██║╚██████╔╝██║ ╚████║ ██║ ██║██║ ██║ 7 | ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ ╚═╝ ╚═╝╚═╝ ╚═╝ 8 | ${AnsiColor.DEFAULT} -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/repository/TokenRepository.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.repository; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.data.jpa.repository.Query; 5 | import vn.webapp.backend.auction.model.Token; 6 | 7 | import java.util.List; 8 | import java.util.Optional; 9 | 10 | public interface TokenRepository extends JpaRepository { 11 | @Query(value = """ 12 | select t from Token t inner join User u\s 13 | on t.user.id = u.id\s 14 | where u.id = :id and (t.expired = false or t.revoked = false)\s 15 | """) 16 | List findAllValidTokenByUser(Integer id); 17 | 18 | Optional findByToken(String token); 19 | Optional findByRefreshToken(String token); 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/model/JewelryCategory.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import jakarta.persistence.*; 5 | import jakarta.validation.constraints.NotBlank; 6 | import lombok.*; 7 | 8 | import java.util.List; 9 | 10 | @AllArgsConstructor 11 | @NoArgsConstructor 12 | @Entity 13 | @Builder 14 | @Data 15 | @Table(name = "jewelry_category") 16 | public class JewelryCategory { 17 | @Id 18 | @GeneratedValue(strategy = GenerationType.IDENTITY) 19 | private int id; 20 | 21 | @Column(name = "name", nullable = false) 22 | @NotBlank(message = "The category name is required") 23 | private String name; 24 | 25 | @OneToMany(mappedBy = "category") 26 | @EqualsAndHashCode.Exclude 27 | @ToString.Exclude 28 | @JsonIgnore 29 | private List jewelries; 30 | } -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/service/bank/BankServiceImpl.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.service.bank; 2 | 3 | import jakarta.transaction.Transactional; 4 | import lombok.RequiredArgsConstructor; 5 | import org.springframework.stereotype.Service; 6 | import vn.webapp.backend.auction.model.Bank; 7 | import vn.webapp.backend.auction.repository.BankRepository; 8 | 9 | import java.util.List; 10 | 11 | @Service 12 | @Transactional 13 | @RequiredArgsConstructor 14 | public class BankServiceImpl implements BankService { 15 | private final BankRepository bankRepository; 16 | 17 | @Override 18 | public List getAll() { 19 | return this.bankRepository.findAll(); 20 | } 21 | 22 | @Override 23 | public Bank getById(Integer id) { 24 | return this.bankRepository.findById(id) 25 | .orElseThrow(() -> new RuntimeException("Bank not found")); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/model/Bank.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import jakarta.persistence.*; 5 | import lombok.*; 6 | 7 | import java.util.List; 8 | 9 | @Entity 10 | @Data 11 | @AllArgsConstructor 12 | @NoArgsConstructor 13 | @Table(name = "bank") 14 | public class Bank { 15 | @Id 16 | @GeneratedValue(strategy = GenerationType.IDENTITY) 17 | private int id; 18 | 19 | @Column(name = "bank_name", unique = true, nullable = false) 20 | private String bankName; 21 | 22 | @Column(name = "trading_name", unique = true, nullable = false) 23 | private String tradingName; 24 | 25 | @Column(name = "logo", nullable = false) 26 | private String logo; 27 | 28 | @OneToMany(mappedBy = "bank") 29 | @EqualsAndHashCode.Exclude 30 | @ToString.Exclude 31 | @JsonIgnore 32 | private List users; 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/controller/DashBoardController.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.controller; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.http.ResponseEntity; 5 | import org.springframework.web.bind.annotation.*; 6 | import vn.webapp.backend.auction.dto.DashBoardRequest; 7 | import vn.webapp.backend.auction.dto.DashBoardResponse; 8 | import vn.webapp.backend.auction.service.dashboard.DashBoardService; 9 | 10 | @RestController 11 | @RequestMapping("/api/v1/dashboard") 12 | @RequiredArgsConstructor 13 | @CrossOrigin(origins = "http://localhost:3000") 14 | public class DashBoardController { 15 | 16 | private final DashBoardService dashBoardService; 17 | 18 | @GetMapping 19 | public ResponseEntity getDashBoardTotal(@ModelAttribute DashBoardRequest dashBoardRequest) { 20 | return ResponseEntity.ok(dashBoardService.getInformation(dashBoardRequest)); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/model/Image.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonBackReference; 4 | import jakarta.persistence.*; 5 | import lombok.*; 6 | 7 | @AllArgsConstructor 8 | @NoArgsConstructor 9 | @Entity 10 | @Data 11 | @Table(name = "image") 12 | public class Image { 13 | 14 | @Id 15 | @GeneratedValue(strategy = GenerationType.IDENTITY) 16 | private int id; 17 | 18 | @Column(name = "icon") 19 | private boolean icon; 20 | 21 | @Column(name = "link") 22 | private String link; 23 | 24 | @Column(name = "data") 25 | @Lob 26 | private String data; 27 | 28 | @ManyToOne(cascade = { 29 | CascadeType.PERSIST, CascadeType.DETACH, 30 | CascadeType.MERGE, CascadeType.REFRESH 31 | }) 32 | @JoinColumn(name = "jewelry_id") 33 | @EqualsAndHashCode.Exclude 34 | @ToString.Exclude 35 | private Jewelry jewelry; 36 | 37 | } -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build Spring Boot Project 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | run-tests: 11 | name: Run Tests 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Checkout code 16 | uses: actions/checkout@v2 17 | 18 | - name: Set up JDK 17 19 | uses: actions/setup-java@v2 20 | with: 21 | java-version: '17' 22 | distribution: 'temurin' 23 | 24 | - name: Build with Maven 25 | run: mvn -B package -DskipTests --file pom.xml 26 | 27 | - name: Distribute jar-war file 28 | run: mkdir staging && cp target/*.jar staging 29 | 30 | - name: Make hyperlink to jar-war file # Tạo hyperlink để download build 31 | uses: actions/upload-artifact@v2 32 | with: 33 | name: 1.0-SNAPSHOT.jar # Bản build được đánh version 1.0-SNAPSHOT 34 | path: staging 35 | 36 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/controller/BankController.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.controller; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.http.ResponseEntity; 5 | import org.springframework.web.bind.annotation.CrossOrigin; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RestController; 9 | import vn.webapp.backend.auction.model.Bank; 10 | import vn.webapp.backend.auction.service.bank.BankService; 11 | 12 | import java.util.List; 13 | 14 | @RestController 15 | @RequestMapping("/api/v1/bank") 16 | @RequiredArgsConstructor 17 | @CrossOrigin(origins = "http://localhost:3000") 18 | public class BankController { 19 | private final BankService bankService; 20 | 21 | @GetMapping 22 | public ResponseEntity> getAll() { 23 | return ResponseEntity.ok(bankService.getAll()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/repository/ImageRepository.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.repository; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.data.jpa.repository.Modifying; 5 | import org.springframework.data.jpa.repository.Query; 6 | import org.springframework.data.repository.query.Param; 7 | import org.springframework.transaction.annotation.Transactional; 8 | import vn.webapp.backend.auction.model.Image; 9 | 10 | import java.util.List; 11 | 12 | 13 | public interface ImageRepository extends JpaRepository { 14 | List findImageByJewelryId(Integer jewelryId); 15 | 16 | @Query("SELECT i FROM Image i WHERE i.icon = true and i.jewelry.id = :jewelryId") 17 | Image findImageByIconAndJewelryId(@Param("jewelryId") Integer jewelryId); 18 | 19 | @Modifying 20 | @Transactional 21 | @Query("DELETE FROM Image i WHERE i.jewelry.id = :jewelryId") 22 | void deleteByJewelryId(@Param("jewelryId") Integer jewelryId); 23 | } 24 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release Auction REST API 2 | 3 | on: 4 | workflow_run: 5 | workflows: [ Build Spring Boot Project ] 6 | types: 7 | - completed 8 | branches: 9 | - main 10 | 11 | permissions: 12 | contents: write 13 | pull-requests: write 14 | 15 | jobs: 16 | release: 17 | name: Release Auction REST API 18 | runs-on: ubuntu-latest 19 | if: ${{ github.event.workflow_run.conclusion == 'success' }} 20 | steps: 21 | - name: Checkout repository 22 | uses: actions/checkout@v4 23 | 24 | - name: Get version 25 | id: get_version 26 | run: | 27 | chmod +x get_version.sh 28 | echo "version=$(./get_version.sh)" >> $GITHUB_OUTPUT 29 | 30 | - name: Release Auction REST API 31 | uses: softprops/action-gh-release@v0.1.15 32 | with: 33 | name: 'Auction REST API - v${{ steps.get_version.outputs.version }}' 34 | tag_name: ${{ steps.get_version.outputs.version }} 35 | generate_release_notes: true 36 | 37 | env: 38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/service/bid/AuctionHistoryService.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.service.bid; 2 | 3 | import org.springframework.data.domain.Page; 4 | import org.springframework.data.domain.Pageable; 5 | import vn.webapp.backend.auction.dto.BidRequest; 6 | import vn.webapp.backend.auction.enums.AuctionHistoryState; 7 | import vn.webapp.backend.auction.model.AuctionHistory; 8 | 9 | import java.util.List; 10 | 11 | public interface AuctionHistoryService { 12 | Page getAuctionHistoryByAuctionId(Pageable pageable, Integer auctionId); 13 | Page getAuctionHistoryByUsername(Pageable pageable, String username); 14 | List getAuctionHistoryByDate(String date); 15 | List getAuctionHistoryByAuctionIdWhenFinished(Integer id); 16 | void saveBidByUserAndAuction(BidRequest request); 17 | void deleteBidByUserAndAuction(Integer userId, Integer auctionId, String reason); 18 | Page getAuctionHistoryByAuctionIdAndUserId(Integer auctionId, Integer userId, AuctionHistoryState state, Pageable pageable); 19 | } 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Phung Huu Thanh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Spring Boot with Railway 2 | 3 | on: 4 | workflow_run: 5 | workflows: [ Publish Auction Backend Docker Image ] 6 | types: 7 | - completed 8 | branches: 9 | - main 10 | 11 | jobs: 12 | build: 13 | name: Build 14 | runs-on: ubuntu-latest 15 | if: ${{ github.event.workflow_run.conclusion == 'success' }} 16 | 17 | steps: 18 | - name: Checkout code 19 | uses: actions/checkout@v2 20 | 21 | - name: Set up JDK 17 22 | uses: actions/setup-java@v2 23 | with: 24 | java-version: '17' 25 | distribution: 'temurin' 26 | 27 | - name: Build with Maven 28 | run: mvn -B package -DskipTests --file pom.xml 29 | 30 | deploy: 31 | name: Deploy 32 | needs: build 33 | runs-on: ubuntu-latest 34 | 35 | steps: 36 | - name: Checkout code 37 | uses: actions/checkout@v2 38 | 39 | - name: Install Railway 40 | run: npm i -g @railway/cli 41 | 42 | - name: Deploy 43 | run: railway up --service web 44 | env: 45 | RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }} 46 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/config/cors/CorsConfiguration.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.config.cors; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 5 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 6 | 7 | @Configuration 8 | public class CorsConfiguration implements WebMvcConfigurer { 9 | 10 | @Override 11 | public void addCorsMappings(CorsRegistry registry) { 12 | registry.addMapping("/api/v1/**") 13 | .allowedOrigins( 14 | "http://localhost:3000", 15 | "http://localhost:3001", 16 | "https://fe-deploy-hazel.vercel.app", 17 | "https://auction-webapp-production.up.railway.app", 18 | "https://admin-manager-auction-production.vercel.app", 19 | "https://website-auction-production.vercel.app" 20 | ) 21 | .allowedMethods("GET", "POST", "PUT", "DELETE") 22 | .allowCredentials(true); 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/model/Token.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.model; 2 | 3 | import jakarta.persistence.*; 4 | import lombok.*; 5 | import org.hibernate.annotations.CreationTimestamp; 6 | import vn.webapp.backend.auction.enums.TokenType; 7 | 8 | import java.time.LocalDateTime; 9 | 10 | @Data 11 | @Builder 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | @Entity 15 | public class Token { 16 | 17 | @Id 18 | @GeneratedValue(strategy = GenerationType.IDENTITY) 19 | public Long id; 20 | 21 | @Column(unique = true) 22 | public String token; 23 | 24 | @Column(unique = true) 25 | public String refreshToken; 26 | 27 | @Enumerated(EnumType.STRING) 28 | public TokenType tokenType = TokenType.BEARER; 29 | 30 | public boolean revoked; 31 | 32 | public boolean expired; 33 | 34 | @ManyToOne(fetch = FetchType.LAZY) 35 | @JoinColumn(name = "user_id") 36 | @EqualsAndHashCode.Exclude 37 | @ToString.Exclude 38 | public User user; 39 | 40 | @CreationTimestamp 41 | public LocalDateTime createdTime; 42 | 43 | public String ipAddress; 44 | 45 | public String deviceInfo; 46 | } -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/config/webSocket/WebSocketConfiguration.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.config.webSocket; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.messaging.simp.config.MessageBrokerRegistry; 5 | import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; 6 | import org.springframework.web.socket.config.annotation.StompEndpointRegistry; 7 | import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; 8 | 9 | @Configuration 10 | @EnableWebSocketMessageBroker 11 | public class WebSocketConfiguration implements WebSocketMessageBrokerConfigurer { 12 | 13 | @Override 14 | public void registerStompEndpoints(StompEndpointRegistry registry) { 15 | registry.addEndpoint("/ws") 16 | .setAllowedOrigins("http://localhost:3000", "https://fe-deploy-hazel.vercel.app") 17 | .withSockJS(); 18 | } 19 | 20 | @Override 21 | public void configureMessageBroker(MessageBrokerRegistry registry) { 22 | registry.enableSimpleBroker("/user"); 23 | registry.setApplicationDestinationPrefixes("/app"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/dto/DashBoardResponse.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @Builder 10 | @AllArgsConstructor 11 | @NoArgsConstructor 12 | public class DashBoardResponse { 13 | private Double totalRevenueToday; 14 | private Integer totalUser; 15 | private Integer totalJewelryPricing; 16 | private Integer totalJewelryPriced; 17 | private Integer totalJewelryNotHasAuction; 18 | private Integer totalJewelryHasAuction; 19 | private Integer totalJewelryHandover; 20 | private Integer totalAuctionJewelry; 21 | private Integer totalUsersVerified; 22 | private Integer totalUsersActive; 23 | private Integer totalUsersInActive; 24 | private Integer totalMembers; 25 | private Integer totalStaffs; 26 | private Integer totalManagers; 27 | private Integer totalAdmins; 28 | private double auctionFailed; 29 | private double auctionSuccess; 30 | private Double[] totalParticipationByMonth; 31 | private Integer[] totalUsersByMonth; 32 | private Integer[] totalAuctionByMonth; 33 | private Double[] totalRevenueNear10Year; 34 | private Double[] totalRevenueByMonth; 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/model/ErrorMessages.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.model; 2 | 3 | 4 | public class ErrorMessages { 5 | public static final String USER_NOT_FOUND = "Không tìm thấy người dùng."; 6 | public static final String CATEGORY_NOT_FOUND = "Danh mục không tồn tại."; 7 | public static final String USER_NOT_VERIFIED = "Người dùng chưa xác thực."; 8 | public static final String USER_ALREADY_EXIST = "Người dùng đã tồn tại."; 9 | public static final String REQUEST_APPROVAL_NOT_FOUND = "Không tìm thấy yêu cầu."; 10 | public static final String JEWELRY_NOT_FOUND = "Không tìm thấy trang sức."; 11 | public static final String JEWELRY_CATEGORY_NOT_FOUND = "Không tìm thấy danh mục trang sức."; 12 | public static final String AUCTION_NOT_FOUND = "Không tìm thấy phiên đấu giá."; 13 | public static final String USER_WINNER_NOT_FOUND = "không tìm thấy người chiến thắng."; 14 | public static final String USER_OWNER_NOT_FOUND = "không tìm thấy chủ tài sản."; 15 | public static final String TRANSACTION_NOT_FOUND = "không tìm thấy giao dịch."; 16 | public static final String TRANSACTION_ALREADY_EXISTS = "Giao dịch đã tồn tại."; 17 | public static final String AUCTION_REGISTRATION_NOT_FOUND = "Không tìm thấy đăng ký phiên đấu giá."; 18 | } 19 | -------------------------------------------------------------------------------- /src/test/java/vn/webapp/backend/auction/AuctionRegistrationServiceTest.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 6 | import vn.webapp.backend.auction.service.auction_registration.AuctionRegistrationService; 7 | 8 | @SpringBootTest 9 | public class AuctionRegistrationServiceTest extends AbstractTestNGSpringContextTests{ 10 | 11 | @Autowired 12 | private AuctionRegistrationService auctionRegistrationService; 13 | 14 | // @Test //LỖI DÒNG 33, DANH SÁCH TRẢ VỀ TRỐNG - CÓ VỂ DO DƯA CHO DB TEST 15 | // public void testFindByAuctionIdAndValidReturnsWell(){ 16 | // // Expected 17 | // Integer id = 1; 18 | // 19 | // //Act 20 | // List auctionRegistrations = auctionRegistrationService.findByAuctionIdAndValid(id); 21 | // 22 | // //Assert 23 | // assertNotNull(auctionRegistrations); 24 | // assertFalse(auctionRegistrations.isEmpty()); 25 | // 26 | // for (AuctionRegistration auctionRegistration : auctionRegistrations) { 27 | // assertEquals(auctionRegistration.getAuction().getId(),id); 28 | // } 29 | // 30 | // } 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/vn/webapp/backend/auction/BankServiceTest.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.ActiveProfiles; 7 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 8 | import static org.testng.Assert.*; 9 | import vn.webapp.backend.auction.model.Bank; 10 | import vn.webapp.backend.auction.service.bank.BankService; 11 | 12 | import java.util.List; 13 | 14 | @SpringBootTest 15 | @ActiveProfiles("test") 16 | class BankServiceTest extends AbstractTestNGSpringContextTests { 17 | 18 | 19 | @Autowired 20 | private BankService bankService; 21 | 22 | @Test 23 | public void testGetAllBankAndReturnsWell() { 24 | List banks = bankService.getAll(); 25 | assertNotNull(banks); 26 | assertFalse(banks.isEmpty()); 27 | } 28 | 29 | @Test 30 | public void testGetBankByIdAndReturnRightBank() { 31 | Bank bank = new Bank(); 32 | bank.setId(1); 33 | Bank foundBank = bankService.getById(bank.getId()); 34 | assertNotNull(foundBank); 35 | assertEquals(foundBank.getBankName(), "Ngân hàng Nông nghiệp và Phát triển Nông thôn Việt Nam"); 36 | } 37 | } -------------------------------------------------------------------------------- /src/test/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=auction 2 | 3 | # Config db 4 | spring.datasource.url=jdbc:sqlserver://localhost:1433;encrypt=true;trustServerCertificate=true;databaseName=DB_AUCTION; 5 | spring.datasource.username=sa 6 | spring.datasource.password=Thanhth@nh1 7 | 8 | # Config create table automatically 9 | spring.jpa.hibernate.ddl-auto=none 10 | 11 | # Config email 12 | spring.mail.host=smtp.gmail.com 13 | spring.mail.port=587 14 | spring.mail.username=daugia.dgs789@gmail.com 15 | spring.mail.password=gzrc jqdz vxjp rwrs 16 | spring.mail.properties.mail.smtp.auth=true 17 | spring.mail.properties.mail.smtp.starttls.enable=true 18 | 19 | # Config vnpay 20 | payment.vnPay.url=https://sandbox.vnpayment.vn/paymentv2/vpcpay.html 21 | payment.vnPay.tmnCode=MV56IQL8 22 | payment.vnPay.secretKey=1EU30GTJ9XXSUNDXF8E3H15HUTZVUIDA 23 | payment.vnPay.returnUrl=http://localhost:8080/api/v1/payment/vn-pay-callback 24 | payment.vnPay.version=2.1.0 25 | payment.vnPay.command=pay 26 | payment.vnPay.orderType=other 27 | 28 | # Config security 29 | application.security.jwt.secret-key=jAkq3Mbc9Tz7X4D5Y6L8Q5C9wK8z2Gf9A6PqS5R8tV3v2O5wF8rM7U6pR4yN3T9q 30 | # Access token expiration time (15 minutes) 31 | application.security.jwt.expiration=900000 32 | 33 | # Refresh token expiration time (1 hour) 34 | application.security.jwt.refresh-token.expiration=3600000 35 | -------------------------------------------------------------------------------- /src/test/java/vn/webapp/backend/auction/RequestApprovalServiceTest.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import vn.webapp.backend.auction.exception.ResourceNotFoundException; 7 | import vn.webapp.backend.auction.model.RequestApproval; 8 | import vn.webapp.backend.auction.service.request_approval.RequestApprovalService; 9 | 10 | import static org.testng.Assert.*; 11 | 12 | @SpringBootTest 13 | public class RequestApprovalServiceTest { 14 | 15 | @Autowired 16 | private RequestApprovalService requestApprovalService; 17 | 18 | @Test 19 | public void testGetRequestByIdReturnsWell(){ 20 | // Expected 21 | Integer id = 1; 22 | 23 | // Act 24 | RequestApproval requestApproval = requestApprovalService.getRequestById(id); 25 | 26 | //Assert 27 | assertNotNull(requestApproval); 28 | assertEquals(requestApproval.getId(), id); 29 | } 30 | 31 | @Test 32 | public void testGetRequestByIdReturnsNull(){ 33 | // Expected 34 | Integer nonExistId = 99; 35 | 36 | //Act and Assert 37 | assertThrows(ResourceNotFoundException.class, () -> requestApprovalService.getRequestById(nonExistId)); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/vn/webapp/backend/auction/AuctionHistoryServiceTest.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 6 | import vn.webapp.backend.auction.service.bid.AuctionHistoryService; 7 | 8 | @SpringBootTest 9 | public class AuctionHistoryServiceTest extends AbstractTestNGSpringContextTests{ 10 | 11 | @Autowired 12 | private AuctionHistoryService auctionHistoryService; 13 | 14 | // @Test //LỖI DÒNG 39, DANH SÁCH TRẢ VỀ TRỐNG - CÓ VỂ DO DƯA CHO DB TEST 15 | // public void testGetAuctionHistoryByDateReturnsWell(){ 16 | // // Expected 17 | // String date = "2024-05-01"; 18 | // LocalDate localDate = LocalDate.parse(date); 19 | // Timestamp timestamp = Timestamp.valueOf(localDate.atStartOfDay()); 20 | // //Act 21 | // List auctionHistories = auctionHistoryService.getAuctionHistoryByDate(date); 22 | // //Assert 23 | // assertNotNull(auctionHistories); 24 | // assertFalse(auctionHistories.isEmpty()); 25 | // for (AuctionHistory auctionHistory : auctionHistories){ 26 | // LocalDate auctionHistoryDate = auctionHistory.getTime().toLocalDateTime().toLocalDate(); 27 | // assertEquals(localDate, auctionHistoryDate); } 28 | // } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/resources/application-test.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=auction 2 | server.port=9999 3 | 4 | # Config banner startup 5 | spring.banner.location=banner.txt 6 | 7 | # Config db 8 | spring.datasource.url=jdbc:sqlserver://localhost:1433;encrypt=true;trustServerCertificate=true;databaseName=DB_AUCTION; 9 | spring.datasource.username=sa 10 | spring.datasource.password=Thanhth@nh1 11 | 12 | # Config create table automatically 13 | spring.jpa.hibernate.ddl-auto=none 14 | 15 | # Config email 16 | spring.mail.host=smtp.gmail.com 17 | spring.mail.port=587 18 | spring.mail.username=daugia.dgs789@gmail.com 19 | spring.mail.password=gzrc jqdz vxjp rwrs 20 | spring.mail.properties.mail.smtp.auth=true 21 | spring.mail.properties.mail.smtp.starttls.enable=true 22 | 23 | # Config vnpay 24 | payment.vnPay.url=https://sandbox.vnpayment.vn/paymentv2/vpcpay.html 25 | payment.vnPay.tmnCode=MV56IQL8 26 | payment.vnPay.secretKey=1EU30GTJ9XXSUNDXF8E3H15HUTZVUIDA 27 | payment.vnPay.returnUrl=http://localhost:8080/api/v1/payment/vn-pay-callback 28 | payment.vnPay.version=2.1.0 29 | payment.vnPay.command=pay 30 | payment.vnPay.orderType=other 31 | 32 | # Config security 33 | application.security.jwt.secret-key=jAkq3Mbc9Tz7X4D5Y6L8Q5C9wK8z2Gf9A6PqS5R8tV3v2O5wF8rM7U6pR4yN3T9q 34 | # Access token expiration time (15 minutes) 35 | application.security.jwt.expiration=900000 36 | 37 | # Refresh token expiration time (1 hour) 38 | application.security.jwt.refresh-token.expiration=3600000 39 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/model/AuctionHistory.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.model; 2 | 3 | import jakarta.persistence.*; 4 | import lombok.*; 5 | import vn.webapp.backend.auction.enums.AuctionHistoryState; 6 | 7 | import java.sql.Timestamp; 8 | 9 | @Entity 10 | @Data 11 | @AllArgsConstructor 12 | @NoArgsConstructor 13 | @Builder 14 | @Table(name = "auction_history") 15 | public class AuctionHistory { 16 | @Id 17 | @GeneratedValue(strategy = GenerationType.IDENTITY) 18 | private int id; 19 | 20 | @Column(name = "price_given", nullable = false) 21 | private double priceGiven; 22 | 23 | @Column(name = "time", nullable = false) 24 | private Timestamp time; 25 | 26 | @Column(name = "bid_code", nullable = false, length = 20) 27 | private String bidCode; 28 | 29 | @Enumerated(EnumType.STRING) 30 | private AuctionHistoryState state; 31 | 32 | @ManyToOne(cascade = { 33 | CascadeType.PERSIST, CascadeType.DETACH, 34 | CascadeType.MERGE, CascadeType.REFRESH 35 | }) 36 | @JoinColumn(name = "user_id") 37 | @EqualsAndHashCode.Exclude 38 | @ToString.Exclude 39 | private User user; 40 | 41 | @ManyToOne(cascade = { 42 | CascadeType.PERSIST, CascadeType.DETACH, 43 | CascadeType.MERGE, CascadeType.REFRESH 44 | }) 45 | @JoinColumn(name = "auction_id") 46 | @EqualsAndHashCode.Exclude 47 | @ToString.Exclude 48 | private Auction auction; 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/service/authenticate/AuthenticationService.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.service.authenticate; 2 | 3 | import jakarta.mail.MessagingException; 4 | import jakarta.servlet.http.HttpServletRequest; 5 | import jakarta.servlet.http.HttpServletResponse; 6 | import vn.webapp.backend.auction.dto.*; 7 | 8 | import java.io.IOException; 9 | 10 | public interface AuthenticationService { 11 | public AuthenticationResponse authenticateGeneral 12 | (AuthenticationRequest request, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws MessagingException; 13 | AuthenticationResponse authenticateAdminManager 14 | (AuthenticationRequest request, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse); 15 | 16 | public void activateAccount(ActivateAccountRequest request) throws MessagingException; 17 | 18 | public AuthenticationResponse register(RegisterAccountRequest request, HttpServletRequest httpServletRequest) throws MessagingException; 19 | 20 | public void refreshToken( 21 | HttpServletRequest request, 22 | HttpServletResponse response 23 | ) throws IOException; 24 | 25 | public AuthenticationResponse changePassword(ChangePasswordRequest request); 26 | 27 | public void forgotPassword(ForgotPasswordRequest request) throws MessagingException; 28 | 29 | public AuthenticationResponse resetPassword(ResetPasswordRequest request); 30 | 31 | } -------------------------------------------------------------------------------- /src/main/resources/application-dev.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=auction 2 | server.port=8080 3 | 4 | frontend.base-url=http://localhost:3000 5 | 6 | # Config banner startup 7 | spring.banner.location=banner.txt 8 | 9 | # Config db 10 | spring.datasource.url=jdbc:sqlserver://localhost:1433;encrypt=true;trustServerCertificate=true;databaseName=DB_AUCTION; 11 | spring.datasource.username=sa 12 | spring.datasource.password=Thanhth@nh1 13 | 14 | # Config create table automatically 15 | spring.jpa.hibernate.ddl-auto=update 16 | 17 | # Config email 18 | spring.mail.host=smtp.gmail.com 19 | spring.mail.port=587 20 | spring.mail.username=daugia.dgs789@gmail.com 21 | spring.mail.password=gzrc jqdz vxjp rwrs 22 | spring.mail.properties.mail.smtp.auth=true 23 | spring.mail.properties.mail.smtp.starttls.enable=true 24 | 25 | # Config vnpay 26 | payment.vnPay.url=https://sandbox.vnpayment.vn/paymentv2/vpcpay.html 27 | payment.vnPay.tmnCode=MV56IQL8 28 | payment.vnPay.secretKey=1EU30GTJ9XXSUNDXF8E3H15HUTZVUIDA 29 | payment.vnPay.returnUrl=http://localhost:8080/api/v1/payment/vn-pay-callback 30 | payment.vnPay.version=2.1.0 31 | payment.vnPay.command=pay 32 | payment.vnPay.orderType=other 33 | 34 | # Config security 35 | application.security.jwt.secret-key=jAkq3Mbc9Tz7X4D5Y6L8Q5C9wK8z2Gf9A6PqS5R8tV3v2O5wF8rM7U6pR4yN3T9q 36 | 37 | # Access token expiration time (15 minutes) 38 | #application.security.jwt.expiration=900000 39 | application.security.jwt.expiration=7200000 40 | 41 | # Refresh token expiration time (1 hour) 42 | #application.security.jwt.refresh-token.expiration=3600000 43 | application.security.jwt.refresh-token.expiration=86400000 -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/controller/ImageController.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.controller; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.http.ResponseEntity; 5 | import org.springframework.web.bind.annotation.*; 6 | import vn.webapp.backend.auction.dto.ImageRequest; 7 | import vn.webapp.backend.auction.model.Image; 8 | import vn.webapp.backend.auction.service.image.ImageService; 9 | 10 | import java.util.List; 11 | 12 | @RestController 13 | @RequestMapping("/api/v1/image") 14 | @RequiredArgsConstructor 15 | @CrossOrigin(origins = "http://localhost:3000") 16 | public class ImageController { 17 | 18 | private final ImageService imageService; 19 | 20 | @GetMapping("/get-by-jewelry/{id}") 21 | public ResponseEntity> getByJewelry(@PathVariable Integer id) { 22 | return ResponseEntity.ok(imageService.getImagesByJewelryId(id)); 23 | } 24 | 25 | @GetMapping("/get-icon-jewelry/{id}") 26 | public ResponseEntity getIconByJewelry(@PathVariable Integer id) { 27 | return ResponseEntity.ok(imageService.getImageByIconAndJewelryId(id)); 28 | } 29 | 30 | @PostMapping("/add-image") 31 | public ResponseEntity createImage(@RequestBody ImageRequest request){ 32 | return ResponseEntity.ok(imageService.createImage(request)); 33 | } 34 | 35 | @DeleteMapping("/jewelry/{jewelryId}") 36 | public ResponseEntity deleteImagesByJewelryId(@PathVariable Integer jewelryId) { 37 | imageService.deleteByJewelryId(jewelryId); 38 | return ResponseEntity.noContent().build(); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/service/request_approval/RequestApprovalService.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.service.request_approval; 2 | 3 | import org.springframework.data.domain.Page; 4 | import org.springframework.data.domain.Pageable; 5 | import vn.webapp.backend.auction.dto.CancelRequestApproval; 6 | import vn.webapp.backend.auction.dto.ManagerRequestApproval; 7 | import vn.webapp.backend.auction.dto.StaffRequestApproval; 8 | import vn.webapp.backend.auction.dto.UserRequestApproval; 9 | import vn.webapp.backend.auction.enums.JewelryState; 10 | import vn.webapp.backend.auction.enums.Role; 11 | import vn.webapp.backend.auction.model.RequestApproval; 12 | 13 | public interface RequestApprovalService { 14 | RequestApproval getRequestById(Integer id); 15 | void setRequestState(Integer id, Integer responderId, String state); 16 | void confirmRequest(Integer id, Integer responderId); 17 | void cancelRequest(CancelRequestApproval request); 18 | Page getRequestBySenderRole(Role role, String jewelryName, String category, Pageable pageable); 19 | RequestApproval requestFromUser(UserRequestApproval request); 20 | RequestApproval requestFromStaff(StaffRequestApproval request); 21 | RequestApproval requestFromManager(ManagerRequestApproval request); 22 | Page getRequestApprovalByUserId(Integer id, String jewelryName, String category, Pageable pageable); 23 | Page getRequestApprovalPassed(String jewelryName, String category, Pageable pageable); 24 | Page getRequestNeedConfirmByMember(Integer memberId,String jewelryName, Pageable pageable); 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/service/user/UserService.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.service.user; 2 | 3 | import org.springframework.data.domain.Page; 4 | import org.springframework.data.domain.Pageable; 5 | import vn.webapp.backend.auction.dto.DisableUserRequest; 6 | import vn.webapp.backend.auction.dto.RegisterAccountRequest; 7 | import vn.webapp.backend.auction.dto.UserSpentResponse; 8 | import vn.webapp.backend.auction.enums.AccountState; 9 | import vn.webapp.backend.auction.enums.Role; 10 | import vn.webapp.backend.auction.model.User; 11 | 12 | import java.util.List; 13 | 14 | public interface UserService { 15 | User findUserByUsernameOrEmail(String username); 16 | User getUserByUsername(String username); 17 | User getUserById(Integer id); 18 | User getUserByEmail(String email); 19 | List getAll(); 20 | List getAllStaff(); 21 | User registerStaff(RegisterAccountRequest request); 22 | void setAccountState(Integer id, AccountState state); 23 | void setDisableAccount(Integer id, DisableUserRequest request); 24 | User updateUser(User user); 25 | Page getMemberByFullNameContainingAndState(String fullName ,AccountState state, Pageable pageable); 26 | Page getStaffByFullNameContainingAndRoleAndState(String fullName, Role role, AccountState state , Pageable page); 27 | User getLatestUserInAuctionHistoryByAuctionId(Integer auctionId); 28 | List getMostSpentUser(); 29 | Page getUsersUnVerifyByFullNameContainingAndState(String fullName, AccountState state, Pageable pageable); 30 | void rejectVerifyUser(Integer id); 31 | List getUserRegistrationAuctionByAuctionId(Integer auctionId); 32 | } 33 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=auction 2 | server.port=${PORT:8080} 3 | 4 | frontend.base-url=https://website-auction-production.vercel.app 5 | 6 | # Config banner startup 7 | spring.banner.location=banner.txt 8 | 9 | # Config db 10 | spring.datasource.url=jdbc:mysql://monorail.proxy.rlwy.net:10597/DB_AUCTION 11 | spring.datasource.username=root 12 | spring.datasource.password=BxfkFKmKqporMffimTRuTptjFAunLXKh 13 | #spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 14 | # Config create table automatically 15 | spring.jpa.hibernate.ddl-auto=none 16 | 17 | # Config email 18 | spring.mail.host=smtp.gmail.com 19 | spring.mail.port=587 20 | spring.mail.username=daugia.dgs789@gmail.com 21 | spring.mail.password=gzrc jqdz vxjp rwrs 22 | spring.mail.properties.mail.smtp.auth=true 23 | spring.mail.properties.mail.smtp.starttls.enable=true 24 | 25 | # Config vnpay 26 | payment.vnPay.url=https://sandbox.vnpayment.vn/paymentv2/vpcpay.html 27 | payment.vnPay.tmnCode=MV56IQL8 28 | payment.vnPay.secretKey=1EU30GTJ9XXSUNDXF8E3H15HUTZVUIDA 29 | payment.vnPay.returnUrl=https://auction-webapp-production.up.railway.app/api/v1/payment/vn-pay-callback 30 | payment.vnPay.version=2.1.0 31 | payment.vnPay.command=pay 32 | payment.vnPay.orderType=other 33 | 34 | # Config security 35 | application.security.jwt.secret-key=jAkq3Mbc9Tz7X4D5Y6L8Q5C9wK8z2Gf9A6PqS5R8tV3v2O5wF8rM7U6pR4yN3T9q 36 | # Access token expiration time (15 minutes) 37 | #application.security.jwt.expiration=900000 38 | application.security.jwt.expiration=3600000 39 | 40 | # Refresh token expiration time (1 hour) 41 | #application.security.jwt.refresh-token.expiration=3600000 42 | application.security.jwt.refresh-token.expiration=86400000 43 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/model/AuctionRegistration.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.model; 2 | 3 | import jakarta.persistence.*; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | import vn.webapp.backend.auction.enums.AuctionRegistrationState; 9 | 10 | import java.sql.Timestamp; 11 | 12 | @Entity 13 | @Data 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | @Builder 17 | @Table(name = "auction_registration") 18 | public class AuctionRegistration { 19 | @Id 20 | @GeneratedValue(strategy = GenerationType.IDENTITY) 21 | private int id; 22 | 23 | @Column(name = "registration_fee", nullable = false) 24 | private double registrationFee; 25 | 26 | @Column(name = "registration_date", nullable = false) 27 | private Timestamp registrationDate; 28 | 29 | @Enumerated(EnumType.STRING) 30 | @Column(name = "auction_registration_state", nullable = false) 31 | private AuctionRegistrationState auctionRegistrationState; 32 | 33 | @Column(name = "kick_reason") 34 | private String kickReason; 35 | 36 | @ManyToOne(cascade = { 37 | CascadeType.PERSIST, CascadeType.DETACH, 38 | CascadeType.MERGE, CascadeType.REFRESH 39 | }) 40 | @JoinColumn(name = "user_id") 41 | private User user; 42 | 43 | @ManyToOne(cascade = { 44 | CascadeType.PERSIST, CascadeType.DETACH, 45 | CascadeType.MERGE, CascadeType.REFRESH 46 | }) 47 | @JoinColumn(name = "auction_id") 48 | private Auction auction; 49 | 50 | @OneToOne(cascade = CascadeType.ALL) 51 | @JoinColumn(name = "transaction_id") 52 | private Transaction transaction; 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/controller/JewelryCategoryController.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.controller; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.http.HttpStatus; 5 | import org.springframework.http.ResponseEntity; 6 | import org.springframework.web.bind.annotation.*; 7 | import vn.webapp.backend.auction.model.JewelryCategory; 8 | import vn.webapp.backend.auction.service.category.JewelryCategoryService; 9 | 10 | import java.util.List; 11 | 12 | @RestController 13 | @CrossOrigin(origins = "http://localhost:3000") 14 | @RequiredArgsConstructor 15 | @RequestMapping("/api/v1/jewelry-category") 16 | public class JewelryCategoryController { 17 | 18 | private final JewelryCategoryService jewelryCategoryService; 19 | 20 | @GetMapping("/get-all") 21 | public ResponseEntity> getAllJewelryCategories() { 22 | return ResponseEntity.ok(jewelryCategoryService.getAll()); 23 | } 24 | 25 | @GetMapping("/id/{id}") 26 | public ResponseEntity getJewelryCategoryById(@PathVariable Integer id) { 27 | return ResponseEntity.ok(jewelryCategoryService.getById(id)); 28 | } 29 | 30 | @PostMapping 31 | public ResponseEntity saveJewelryCategory(@RequestBody JewelryCategory jewelryCategory) { 32 | return ResponseEntity.status(HttpStatus.CREATED).body(jewelryCategoryService.saveJewelryCategory(jewelryCategory)); 33 | } 34 | 35 | @DeleteMapping("/{id}") 36 | public ResponseEntity deleteJewelryCategory(@PathVariable Integer id) { 37 | jewelryCategoryService.deleteJewelryCategory(id); 38 | return ResponseEntity.noContent().build(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/service/category/JewelryCategoryServiceImpl.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.service.category; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.stereotype.Service; 5 | import vn.webapp.backend.auction.exception.ResourceNotFoundException; 6 | import vn.webapp.backend.auction.model.ErrorMessages; 7 | import vn.webapp.backend.auction.model.JewelryCategory; 8 | import vn.webapp.backend.auction.repository.JewelryCategoryRepository; 9 | 10 | import java.util.List; 11 | 12 | @Service 13 | @RequiredArgsConstructor 14 | public class JewelryCategoryServiceImpl implements JewelryCategoryService { 15 | 16 | private final JewelryCategoryRepository jewelryCategoryRepository; 17 | 18 | @Override 19 | public List getAll() { 20 | return jewelryCategoryRepository.findAll(); 21 | } 22 | 23 | @Override 24 | public JewelryCategory getById(int id) { 25 | return jewelryCategoryRepository.findById(id) 26 | .orElseThrow(() -> new ResourceNotFoundException(ErrorMessages.JEWELRY_CATEGORY_NOT_FOUND)); 27 | } 28 | 29 | @Override 30 | public JewelryCategory saveJewelryCategory(JewelryCategory jewelryCategory) { 31 | jewelryCategory.setId(0); 32 | return jewelryCategoryRepository.save(jewelryCategory); 33 | } 34 | 35 | @Override 36 | public void deleteJewelryCategory(Integer id) { 37 | var existingBookCategory = jewelryCategoryRepository.findById(id) 38 | .orElseThrow(() -> new ResourceNotFoundException(ErrorMessages.JEWELRY_CATEGORY_NOT_FOUND)); 39 | existingBookCategory.getJewelries() 40 | .forEach((book) -> book.setCategory(null)); 41 | jewelryCategoryRepository.deleteById(id); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/service/auction/AuctionService.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.service.auction; 2 | 3 | import jakarta.mail.MessagingException; 4 | import org.springframework.data.domain.Page; 5 | import org.springframework.data.domain.Pageable; 6 | import vn.webapp.backend.auction.dto.AuctionRegistrationResponse; 7 | import vn.webapp.backend.auction.dto.AuctionRequest; 8 | import vn.webapp.backend.auction.enums.AuctionState; 9 | import vn.webapp.backend.auction.model.Auction; 10 | 11 | import java.util.List; 12 | 13 | public interface AuctionService { 14 | List getAll(); 15 | 16 | Auction getAuctionById(Integer id); 17 | 18 | void deleteAuction(Integer id); 19 | 20 | List findAuctionByName(String name); 21 | 22 | Page getAllAuctions(AuctionState state, Pageable pageable, String auctionName, Integer categoryId); 23 | 24 | void setAuctionState(Integer id, String state); 25 | 26 | Page getAuctionsByStates(List states, Pageable pageable); 27 | 28 | List getAuctionByState(AuctionState state); 29 | 30 | Page findAuctionSortByBetweenStartDayAndEndDay(String startDay, String endDay, Pageable pageable); 31 | 32 | Page getByStaffID(Integer id, String auctionName, Pageable pageable); 33 | 34 | Auction getCurrentAuctionByJewelryId(Integer id); 35 | 36 | Auction createNewAuction(AuctionRequest request); 37 | 38 | void deleteAuctionResult(Integer transactionId) throws MessagingException; 39 | 40 | Page getAuctionRegistrations(AuctionState state, String auctionName, Pageable pageable); 41 | 42 | Page getAllFailedAuctions(Pageable pageable, String auctionName); 43 | 44 | Auction updateEndTimeAuction(Integer auction, Long time); 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/service/transaction/TransactionService.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.service.transaction; 2 | 3 | import org.springframework.data.domain.Page; 4 | import org.springframework.data.domain.Pageable; 5 | import vn.webapp.backend.auction.dto.UserTransactionResponse; 6 | import vn.webapp.backend.auction.enums.TransactionState; 7 | import vn.webapp.backend.auction.enums.TransactionType; 8 | import vn.webapp.backend.auction.model.Transaction; 9 | import vn.webapp.backend.auction.model.User; 10 | 11 | import java.util.List; 12 | 13 | public interface TransactionService { 14 | List getAll(); 15 | 16 | Transaction getTransactionById(Integer id); 17 | 18 | UserTransactionResponse getTransactionsDashboardByUsername(String username); 19 | 20 | void setTransactionState(Integer id, String state); 21 | 22 | void setTransactionStateWithCode(Integer id, String state, String transactionCode, String bankCode); 23 | 24 | 25 | void setTransactionMethod(Integer id, String method); 26 | 27 | Page getTransactionsByUsername (String username, String assetName, Pageable pageable); 28 | 29 | User createTransactionForWinner(Integer auctionId); 30 | 31 | User createTransactionForSeller(Integer auctionId); 32 | 33 | Page getTransactionByTypeAndState (TransactionType typename, String userName,TransactionState state, Pageable pageable); 34 | 35 | Page getTransactionHandover (TransactionType typename, String jewelryName, String category, Pageable pageable); 36 | 37 | List createTransactionForWinnerIfNotExists(Integer userId); 38 | 39 | Page getOverdueTransactions(String userName,Pageable pageable); 40 | 41 | void setTransactionAfterPaySuccess(Integer transactionId, String transactionCode, String bankCode); 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/service/image/ImageServiceImpl.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.service.image; 2 | 3 | import jakarta.transaction.Transactional; 4 | import lombok.RequiredArgsConstructor; 5 | import org.springframework.stereotype.Service; 6 | import vn.webapp.backend.auction.dto.ImageRequest; 7 | import vn.webapp.backend.auction.model.Image; 8 | import vn.webapp.backend.auction.model.Jewelry; 9 | import vn.webapp.backend.auction.repository.ImageRepository; 10 | import vn.webapp.backend.auction.repository.JewelryRepository; 11 | 12 | import java.util.List; 13 | import java.util.Optional; 14 | 15 | @Transactional 16 | @Service 17 | @RequiredArgsConstructor 18 | public class ImageServiceImpl implements ImageService { 19 | private final ImageRepository imageRepository; 20 | private final JewelryRepository jewelryRepository; 21 | 22 | 23 | @Override 24 | public List getImagesByJewelryId(Integer id) { 25 | return imageRepository.findImageByJewelryId(id); 26 | } 27 | 28 | @Override 29 | public Image getImageByIconAndJewelryId(Integer id) { 30 | return imageRepository.findImageByIconAndJewelryId(id); 31 | } 32 | 33 | @Override 34 | public Image createImage(ImageRequest request) { 35 | Optional existJewelry = jewelryRepository.findById(request.jewelryId()); 36 | if (existJewelry.isEmpty()) { 37 | throw new IllegalArgumentException("Jewelry with ID " + request.jewelryId() + " not found"); 38 | } 39 | Jewelry jewelry = existJewelry.get(); 40 | Image image = new Image(); 41 | image.setIcon(request.icon()); 42 | image.setData(request.data()); 43 | image.setJewelry(jewelry); 44 | imageRepository.save(image); 45 | return image; 46 | } 47 | 48 | @Transactional 49 | public void deleteByJewelryId(Integer jewelryId) { 50 | imageRepository.deleteByJewelryId(jewelryId); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/service/logout/LogoutService.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.service.logout; 2 | 3 | import jakarta.servlet.http.HttpServletRequest; 4 | import jakarta.servlet.http.HttpServletResponse; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.security.core.Authentication; 7 | import org.springframework.security.core.context.SecurityContextHolder; 8 | import org.springframework.stereotype.Service; 9 | import org.springframework.security.web.authentication.logout.LogoutHandler; 10 | import vn.webapp.backend.auction.repository.TokenRepository; 11 | import org.springframework.http.ResponseCookie; 12 | 13 | @Service 14 | @RequiredArgsConstructor 15 | public class LogoutService implements LogoutHandler { 16 | 17 | private final TokenRepository tokenRepository; 18 | 19 | @Override 20 | public void logout( 21 | HttpServletRequest request, 22 | HttpServletResponse response, 23 | Authentication authentication 24 | ) { 25 | final String authHeader = request.getHeader("Authorization"); 26 | final String jwt; 27 | if (authHeader == null ||!authHeader.startsWith("Bearer ")) { 28 | return; 29 | } 30 | jwt = authHeader.substring(7); 31 | var storedToken = tokenRepository.findByToken(jwt) 32 | .orElse(null); 33 | if (storedToken != null) { 34 | storedToken.setExpired(true); 35 | storedToken.setRevoked(true); 36 | tokenRepository.save(storedToken); 37 | SecurityContextHolder.clearContext(); 38 | } 39 | 40 | ResponseCookie refreshTokenCookie = ResponseCookie.from("refresh_token", "") 41 | .httpOnly(true) 42 | .secure(true) // Set to true if you're using HTTPS 43 | .path("/") 44 | .maxAge(0) // Expire the cookie immediately 45 | .build(); 46 | 47 | response.addHeader("Set-Cookie", refreshTokenCookie.toString()); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/controller/RealTimeController.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.controller; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.messaging.handler.annotation.MessageExceptionHandler; 5 | import org.springframework.messaging.handler.annotation.MessageMapping; 6 | import org.springframework.messaging.handler.annotation.Payload; 7 | import org.springframework.messaging.handler.annotation.SendTo; 8 | import org.springframework.web.bind.annotation.CrossOrigin; 9 | import org.springframework.web.bind.annotation.RestController; 10 | import vn.webapp.backend.auction.dto.BidMessage; 11 | import vn.webapp.backend.auction.dto.BidResponse; 12 | import vn.webapp.backend.auction.dto.KickOutMessage; 13 | import vn.webapp.backend.auction.dto.KickOutResponse; 14 | import vn.webapp.backend.auction.service.realtime.RealTimeService; 15 | 16 | import java.util.logging.Logger; 17 | 18 | @RestController 19 | @CrossOrigin(origins = "http://localhost:3000") 20 | @RequiredArgsConstructor 21 | public class RealTimeController { 22 | 23 | private final RealTimeService realTimeService; 24 | Logger logger = Logger.getLogger(getClass().getName()); 25 | 26 | 27 | @MessageMapping("/update-auction") 28 | @SendTo("/user/auction") 29 | public BidResponse sendMessage(@Payload BidMessage message) { 30 | Integer auctionId = message.auctionId(); 31 | Long bonusTime = message.bonusTime(); 32 | String username = message.username(); 33 | 34 | return realTimeService.bidRealtime(auctionId, bonusTime, username); 35 | } 36 | 37 | @MessageMapping("/kick-out-user") 38 | @SendTo("/user/out-auction-registration") 39 | public KickOutResponse sendMessage(@Payload KickOutMessage message) { 40 | Integer auctionId = message.auctionId(); 41 | String username = message.username(); 42 | return realTimeService.staffKickOutMemberRealtime(auctionId, username); 43 | } 44 | 45 | @MessageExceptionHandler 46 | public void handleException(Exception ex) { 47 | logger.info("An error occurred: " + ex.getMessage()); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/controller/AuctionRegistrationController.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.controller; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.data.domain.Page; 5 | import org.springframework.data.domain.PageRequest; 6 | import org.springframework.data.domain.Pageable; 7 | import org.springframework.data.domain.Sort; 8 | import org.springframework.http.ResponseEntity; 9 | import org.springframework.web.bind.annotation.*; 10 | import vn.webapp.backend.auction.model.AuctionRegistration; 11 | import vn.webapp.backend.auction.service.auction_registration.AuctionRegistrationService; 12 | 13 | import java.util.List; 14 | 15 | @RestController 16 | @CrossOrigin(origins = "http://localhost:3000") 17 | @RequiredArgsConstructor 18 | @RequestMapping("/api/v1/auction-registration") 19 | public class AuctionRegistrationController { 20 | 21 | private final AuctionRegistrationService auctionRegistrationService; 22 | 23 | @GetMapping("/auction/{auctionId}") 24 | public ResponseEntity> getRegistrationsForAuction(@PathVariable Integer auctionId) { 25 | List registrations = auctionRegistrationService.findByAuctionIdAndValid(auctionId); 26 | return ResponseEntity.ok(registrations); 27 | } 28 | 29 | @GetMapping("/get-by-user") 30 | public ResponseEntity> getAuctionRegistrationsByUser( 31 | @RequestParam(defaultValue = "registrationDate") String sortBy, 32 | @RequestParam(defaultValue = "5") int size, 33 | @RequestParam(required = false) String auctionName, 34 | @RequestParam(defaultValue = "0") int page, 35 | @RequestParam(defaultValue = "0") int userId, 36 | @RequestParam(defaultValue = "desc") String sortOrder) { 37 | Sort.Direction direction = (sortOrder.equalsIgnoreCase("desc")) ? Sort.Direction.DESC : Sort.Direction.ASC; 38 | Pageable pageable = PageRequest.of(page, size, direction, sortBy); 39 | return ResponseEntity.ok(auctionRegistrationService.findByUserIdAndValid(userId,auctionName, pageable)); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/service/jewelry/JewelryService.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.service.jewelry; 2 | 3 | import jakarta.mail.MessagingException; 4 | import org.springframework.data.domain.Page; 5 | import org.springframework.data.domain.Pageable; 6 | import vn.webapp.backend.auction.dto.JewelryCreateRequest; 7 | import vn.webapp.backend.auction.dto.JewelryUpdateRequest; 8 | import vn.webapp.backend.auction.dto.SendJewelryFromUserRequest; 9 | import vn.webapp.backend.auction.enums.JewelryState; 10 | import vn.webapp.backend.auction.model.Jewelry; 11 | 12 | import java.util.List; 13 | 14 | public interface JewelryService { 15 | List getAll(); 16 | 17 | Jewelry getJewelryById(Integer id); 18 | 19 | List getJewelryByUsername(String username); 20 | 21 | void deleteJewelry(Integer id); 22 | 23 | List getJewelriesByCategoryId(Integer id); 24 | 25 | Page getJewelryPassed(String jewelryName, String category, Pageable pageable); 26 | 27 | List getJewelriesByNameContain(String key); 28 | 29 | Page getAllJewelries(Pageable pageable); 30 | 31 | Page getJewelriesManager(JewelryState state, String jewelryName, String category, Pageable pageable); 32 | 33 | Page getJewelriesInWaitList(Pageable pageable); 34 | 35 | Page getJewelryByStateAndIsHolding(JewelryState state, Boolean isHolding, String category, String jewelryName, Pageable pageable); 36 | 37 | Page getJewelryReturnedViolator(String category, String jewelryName, Pageable pageable); 38 | 39 | Page getJewelriesInHandOver(Pageable pageable); 40 | 41 | Page getJewelriesByUsername(String username, Pageable pageable); 42 | 43 | Page getJewelriesActiveByUserId(Integer userId, String jewelryName, Pageable pageable); 44 | 45 | Jewelry requestJewelry(SendJewelryFromUserRequest request); 46 | 47 | Jewelry getLatestJewelry(); 48 | 49 | Jewelry updateJewelry(JewelryUpdateRequest jewelry); 50 | 51 | Jewelry createJewelry(JewelryCreateRequest jewelry); 52 | 53 | Jewelry setStateWithHolding(Integer id, boolean isHolding, JewelryState state) throws MessagingException; 54 | 55 | } -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/config/application/ApplicationConfiguration.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.config.application; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.security.authentication.AuthenticationManager; 7 | import org.springframework.security.authentication.AuthenticationProvider; 8 | import org.springframework.security.authentication.dao.DaoAuthenticationProvider; 9 | import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; 10 | import org.springframework.security.core.userdetails.UserDetailsService; 11 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 12 | import vn.webapp.backend.auction.exception.ResourceNotFoundException; 13 | import vn.webapp.backend.auction.repository.UserRepository; 14 | 15 | @Configuration 16 | @RequiredArgsConstructor 17 | public class ApplicationConfiguration { 18 | 19 | private final UserRepository userRepository; 20 | 21 | @Bean 22 | public UserDetailsService userDetailsService() { 23 | return usernameOrEmail -> userRepository.findByUsername(usernameOrEmail) 24 | .orElseGet(() -> userRepository.findByEmail(usernameOrEmail) 25 | .orElseThrow(() -> new ResourceNotFoundException( 26 | "Người dùng với username hoặc email không tồn tại. Vui lòng đăng ký tài khoản mới."))); 27 | } 28 | 29 | @Bean 30 | public BCryptPasswordEncoder passwordEncoder() { 31 | return new BCryptPasswordEncoder(); 32 | } 33 | 34 | @Bean 35 | public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception { 36 | return config.getAuthenticationManager(); 37 | } 38 | 39 | @Bean 40 | public AuthenticationProvider authenticationProvider() { 41 | DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); 42 | authProvider.setUserDetailsService(userDetailsService()); 43 | authProvider.setPasswordEncoder(passwordEncoder()); 44 | return authProvider; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/test/java/vn/webapp/backend/auction/ImageServiceTest.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 7 | import vn.webapp.backend.auction.model.Image; 8 | import vn.webapp.backend.auction.service.image.ImageService; 9 | 10 | import java.util.List; 11 | 12 | import static org.testng.Assert.*; 13 | 14 | @SpringBootTest 15 | public class ImageServiceTest extends AbstractTestNGSpringContextTests{ 16 | 17 | @Autowired 18 | private ImageService imageService; 19 | 20 | @Test 21 | public void testGetImagesByJewelryIdReturnsWell(){ 22 | // Expected 23 | Integer id = 1; 24 | 25 | //Act 26 | List images = imageService.getImagesByJewelryId(id); 27 | 28 | //Assert 29 | assertNotNull(images); 30 | assertFalse(images.isEmpty()); 31 | 32 | for (Image image : images) { 33 | assertEquals(image.getJewelry().getId(), id); 34 | } 35 | } 36 | 37 | @Test 38 | public void testGetImagesByJewelryIdReturnsNull(){ 39 | // Expected 40 | Integer nonExistId = 99; 41 | 42 | // Act 43 | List images = imageService.getImagesByJewelryId(nonExistId); 44 | 45 | //Assert 46 | assertNotNull(images); 47 | assertTrue(images.isEmpty()); 48 | } 49 | 50 | @Test 51 | public void testGetImageByIconAndJewelryIdReturnsWell(){ 52 | // Expected 53 | Integer id = 1; 54 | 55 | // Act 56 | Image image = imageService.getImageByIconAndJewelryId(id); 57 | 58 | // Assert 59 | assertNotNull(image); 60 | assertEquals(image.getId(), id); 61 | } 62 | 63 | // @Test CHƯA LÀM TRẢ VỀ 64 | // public void testGetImageByIconAndJewelryIdReturnsNull(){ 65 | // // Expected 66 | // Integer nonExistId = 99; 67 | // 68 | // // Act and Assert 69 | // assertThrows(ResourceNotFoundException.class, () -> imageService.getImageByIconAndJewelryId(nonExistId)); 70 | // 71 | // } 72 | } 73 | -------------------------------------------------------------------------------- /.github/workflows/docker-publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish Auction Backend Docker Image 2 | 3 | on: 4 | workflow_run: 5 | workflows: [ Release Auction REST API ] 6 | types: 7 | - completed 8 | branches: 9 | - main 10 | 11 | jobs: 12 | publish: 13 | name: Publish Auction Backend Docker Image 14 | runs-on: ubuntu-latest 15 | if: ${{ github.event.workflow_run.conclusion == 'success' }} 16 | permissions: 17 | contents: read 18 | packages: write 19 | 20 | steps: 21 | - name: 'Checkout repository' 22 | uses: actions/checkout@v4 23 | 24 | - name: 'Set up JDK 17' 25 | uses: actions/setup-java@v3 26 | with: 27 | java-version: '17' 28 | distribution: 'temurin' 29 | architecture: 'x64' 30 | 31 | - name: 'Build project with Maven' 32 | run: | 33 | mvn verify -DskipTests 34 | 35 | - name: Get version 36 | id: get_version 37 | run: | 38 | chmod +x get_version.sh 39 | echo "version=$(./get_version.sh)" >> $GITHUB_OUTPUT 40 | 41 | - name: Login to GitHub Container Registry 42 | uses: docker/login-action@v3 43 | with: 44 | registry: ghcr.io 45 | username: ${{ github.actor }} 46 | password: ${{ secrets.GHCR_PAT }} 47 | 48 | - name: 'Publish Auction Database Docker Image' 49 | working-directory: ${{ github.workspace }}/db 50 | run: | 51 | docker build . -t ghcr.io/phuuthanh-dev/auction-database:${{ steps.get_version.outputs.version }} -t ghcr.io/phuuthanh-dev/auction-database:latest 52 | docker push ghcr.io/phuuthanh-dev/auction-database:${{ steps.get_version.outputs.version }} 53 | docker push ghcr.io/phuuthanh-dev/auction-database:latest 54 | 55 | 56 | - name: 'Publish Auction Backend Docker Image' 57 | working-directory: ${{ github.workspace }} 58 | run: | 59 | docker build . -t ghcr.io/phuuthanh-dev/auction-api:${{ steps.get_version.outputs.version }} -t ghcr.io/phuuthanh-dev/auction-api:latest 60 | docker push ghcr.io/phuuthanh-dev/auction-api:${{ steps.get_version.outputs.version }} 61 | docker push ghcr.io/phuuthanh-dev/auction-api:latest 62 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/model/RequestApproval.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.model; 2 | 3 | import jakarta.persistence.*; 4 | import lombok.*; 5 | import vn.webapp.backend.auction.enums.RequestApprovalState; 6 | 7 | import java.sql.Timestamp; 8 | 9 | @AllArgsConstructor 10 | @NoArgsConstructor 11 | @Entity 12 | @Builder 13 | @Data 14 | @Table(name = "request_approval") 15 | public class RequestApproval { 16 | @Id 17 | @GeneratedValue(strategy = GenerationType.IDENTITY) 18 | private int id; 19 | 20 | @ManyToOne(cascade = { 21 | CascadeType.PERSIST, CascadeType.DETACH, 22 | CascadeType.MERGE, CascadeType.REFRESH 23 | }) 24 | @JoinColumn(name = "staff_id") 25 | @EqualsAndHashCode.Exclude 26 | @ToString.Exclude 27 | private User staff; 28 | 29 | @ManyToOne(cascade = { 30 | CascadeType.PERSIST, CascadeType.DETACH, 31 | CascadeType.MERGE, CascadeType.REFRESH 32 | }) 33 | @JoinColumn(name = "user_id_send") 34 | @EqualsAndHashCode.Exclude 35 | @ToString.Exclude 36 | private User sender; 37 | 38 | @ManyToOne(cascade = { 39 | CascadeType.PERSIST, CascadeType.DETACH, 40 | CascadeType.MERGE, CascadeType.REFRESH 41 | }) 42 | @JoinColumn(name = "user_id_respond") 43 | @EqualsAndHashCode.Exclude 44 | @ToString.Exclude 45 | private User responder; 46 | 47 | @ManyToOne(cascade = { 48 | CascadeType.PERSIST, CascadeType.DETACH, 49 | CascadeType.MERGE, CascadeType.REFRESH 50 | }) 51 | @JoinColumn(name = "jewelry_id") 52 | @EqualsAndHashCode.Exclude 53 | @ToString.Exclude 54 | private Jewelry jewelry; 55 | 56 | @Column(name = "valuation") 57 | private double valuation; 58 | 59 | @Column(name = "desired_price", nullable = false) 60 | private double desiredPrice; 61 | 62 | @Column(name = "request_time", nullable = false) 63 | private Timestamp requestTime; 64 | 65 | @Column(name = "response_time") 66 | private Timestamp responseTime; 67 | 68 | @Enumerated(EnumType.STRING) 69 | private RequestApprovalState state; 70 | 71 | @Column(name = "is_confirm") 72 | private boolean isConfirm; 73 | 74 | @Column(name = "note") 75 | private String note; 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/service/payment/PaymentService.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.service.payment; 2 | 3 | import jakarta.servlet.http.HttpServletRequest; 4 | import lombok.RequiredArgsConstructor; 5 | import org.springframework.stereotype.Service; 6 | import vn.webapp.backend.auction.config.vnpay.VNPAYConfiguration; 7 | import vn.webapp.backend.auction.dto.PaymentResponse; 8 | import vn.webapp.backend.auction.service.vnpay.VNPayUtil; 9 | 10 | import java.util.Map; 11 | 12 | @Service 13 | @RequiredArgsConstructor 14 | public class PaymentService { 15 | private final VNPAYConfiguration vnPayConfig; 16 | 17 | public PaymentResponse.VNPayResponse createVnPayPayment(HttpServletRequest request) { 18 | long amount = Integer.parseInt(request.getParameter("amount")) * 100L; 19 | String bankCode = request.getParameter("bankCode"); 20 | String auctionId = request.getParameter("auctionId"); 21 | String username = request.getParameter("username"); 22 | String transactionId = request.getParameter("transactionId"); 23 | Map vnpParamsMap; 24 | 25 | if (transactionId != null) { 26 | int id = Integer.parseInt(transactionId); 27 | vnpParamsMap = vnPayConfig.getVNPayConfig(auctionId, username, id); 28 | } else { 29 | vnpParamsMap = vnPayConfig.getVNPayConfig(auctionId, username, 0); 30 | } 31 | vnpParamsMap.put("vnp_Amount", String.valueOf(amount)); 32 | if (bankCode != null && !bankCode.isEmpty()) { 33 | vnpParamsMap.put("vnp_BankCode", bankCode); 34 | } 35 | vnpParamsMap.put("vnp_IpAddr", VNPayUtil.getIpAddress(request)); 36 | 37 | // Build query URL 38 | String queryUrl = VNPayUtil.getPaymentURL(vnpParamsMap, true); 39 | String hashData = VNPayUtil.getPaymentURL(vnpParamsMap, false); 40 | String vnpSecureHash = VNPayUtil.hmacSHA512(vnPayConfig.getSecretKey(), hashData); 41 | queryUrl += "&vnp_SecureHash=" + vnpSecureHash; 42 | String paymentUrl = vnPayConfig.getVnpPayUrl() + "?" + queryUrl; 43 | 44 | return PaymentResponse.VNPayResponse.builder() 45 | .code("ok") 46 | .message("success") 47 | .paymentUrl(paymentUrl).build(); 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/model/Transaction.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.model; 2 | 3 | import jakarta.persistence.*; 4 | import jakarta.validation.constraints.Min; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | import vn.webapp.backend.auction.enums.PaymentMethod; 10 | import vn.webapp.backend.auction.enums.TransactionState; 11 | import vn.webapp.backend.auction.enums.TransactionType; 12 | 13 | import java.sql.Timestamp; 14 | 15 | @AllArgsConstructor 16 | @NoArgsConstructor 17 | @Entity 18 | @Builder 19 | @Data 20 | @Table(name = "[transaction]") 21 | public class Transaction { 22 | 23 | @Id 24 | @GeneratedValue(strategy = GenerationType.IDENTITY) 25 | private int id; 26 | 27 | @Column(name = "create_date", nullable = false) 28 | private Timestamp createDate; 29 | 30 | @Column(name = "payment_time") 31 | private Timestamp paymentTime; 32 | 33 | @Column(name = "total_price", nullable = false) 34 | @Min(value = 1, message = "The total price must be at least 1") 35 | private Double totalPrice; 36 | 37 | @Column(name = "fees_incurred", nullable = false) 38 | @Min(value = 0, message = "The fees incurred must be at least 0") 39 | private Double feesIncurred; 40 | 41 | @Enumerated(EnumType.STRING) 42 | @Column(name = "transaction_state", nullable = false) 43 | private TransactionState state; 44 | 45 | @Enumerated(EnumType.STRING) 46 | @Column(name = "transaction_type", nullable = false) 47 | private TransactionType type; 48 | 49 | @Column(name = "transaction_code") 50 | private String transactionCode; 51 | 52 | @Column(name = "bank_code") 53 | private String bankCode; 54 | 55 | @ManyToOne(cascade = { 56 | CascadeType.PERSIST, CascadeType.DETACH, 57 | CascadeType.MERGE, CascadeType.REFRESH 58 | }) 59 | @JoinColumn(name = "auction_id") 60 | private Auction auction; 61 | 62 | @ManyToOne(cascade = { 63 | CascadeType.PERSIST, CascadeType.DETACH, 64 | CascadeType.MERGE, CascadeType.REFRESH 65 | }) 66 | @JoinColumn(name = "user_id") 67 | private User user; 68 | 69 | @Enumerated(EnumType.STRING) 70 | @Column(name ="payment_method", nullable = true, length = 20) 71 | private PaymentMethod paymentMethod; 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/config/vnpay/VNPAYConfiguration.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.config.vnpay; 2 | import lombok.Getter; 3 | import org.springframework.beans.factory.annotation.Value; 4 | import org.springframework.context.annotation.Configuration; 5 | import vn.webapp.backend.auction.service.vnpay.VNPayUtil; 6 | 7 | import java.text.SimpleDateFormat; 8 | import java.util.*; 9 | 10 | @Configuration 11 | public class VNPAYConfiguration { 12 | @Getter 13 | @Value("${payment.vnPay.url}") 14 | private String vnpPayUrl; 15 | @Value("${payment.vnPay.returnUrl}") 16 | private String vnpReturnUrl; 17 | @Value("${payment.vnPay.tmnCode}") 18 | private String vnpTmnCode ; 19 | @Getter 20 | @Value("${payment.vnPay.secretKey}") 21 | private String secretKey; 22 | @Value("${payment.vnPay.version}") 23 | private String vnpVersion; 24 | @Value("${payment.vnPay.command}") 25 | private String vnpCommand; 26 | @Value("${payment.vnPay.orderType}") 27 | private String orderType; 28 | 29 | public Map getVNPayConfig(String auctionId, String username, Integer transactionId) { 30 | Map vnpParamsMap = new HashMap<>(); 31 | vnpParamsMap.put("vnp_Version", this.vnpVersion); 32 | vnpParamsMap.put("vnp_Command", this.vnpCommand); 33 | vnpParamsMap.put("vnp_TmnCode", this.vnpTmnCode); 34 | vnpParamsMap.put("vnp_CurrCode", "VND"); 35 | vnpParamsMap.put("vnp_TxnRef", VNPayUtil.getRandomNumber(8)); 36 | vnpParamsMap.put("vnp_OrderInfo", "Thanh toan don hang:" + VNPayUtil.getRandomNumber(8)); 37 | vnpParamsMap.put("vnp_OrderType", this.orderType); 38 | vnpParamsMap.put("vnp_Locale", "vn"); 39 | vnpParamsMap.put("vnp_ReturnUrl", this.vnpReturnUrl + "?auctionId=" + auctionId + "&username=" + username + "&transactionId=" + transactionId); 40 | 41 | TimeZone timeZone = TimeZone.getTimeZone("Asia/Ho_Chi_Minh"); 42 | Calendar calendar = Calendar.getInstance(timeZone); 43 | SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss"); 44 | formatter.setTimeZone(timeZone); 45 | 46 | String vnpCreateDate = formatter.format(calendar.getTime()); 47 | vnpParamsMap.put("vnp_CreateDate", vnpCreateDate); 48 | 49 | calendar.add(Calendar.MINUTE, 15); 50 | String vnpExpireDate = formatter.format(calendar.getTime()); 51 | 52 | vnpParamsMap.put("vnp_ExpireDate", vnpExpireDate); 53 | return vnpParamsMap; 54 | } 55 | } -------------------------------------------------------------------------------- /src/test/java/vn/webapp/backend/auction/UserServiceTest.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.ActiveProfiles; 7 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 8 | import vn.webapp.backend.auction.exception.ResourceNotFoundException; 9 | import vn.webapp.backend.auction.model.User; 10 | import vn.webapp.backend.auction.service.user.UserService; 11 | 12 | import static org.testng.Assert.*; 13 | 14 | 15 | 16 | @SpringBootTest 17 | @ActiveProfiles("test") 18 | public class UserServiceTest extends AbstractTestNGSpringContextTests { 19 | 20 | @Autowired 21 | private UserService userService; 22 | 23 | @Test 24 | public void testGetUserByUsernameReturnsWell() { 25 | // Expected 26 | String username = "phuuthanh2003"; 27 | // Act 28 | User user = userService.getUserByUsername(username); 29 | // Assert 30 | assertNotNull(user); 31 | assertEquals(user.getUsername(), username); 32 | } 33 | 34 | @Test 35 | public void testGetUserByIdReturnsWell() { 36 | // Expected 37 | Integer id = 1; 38 | 39 | // Act 40 | User user = userService.getUserById(id); 41 | 42 | // Assert 43 | assertNotNull(user); 44 | assertEquals(user.getId(), id); 45 | } 46 | 47 | @Test 48 | public void testGetUserByEmailReturnsWell() { 49 | // Expected 50 | String email = "phuuthanh2003@gmail.com"; 51 | // Act 52 | User user = userService.getUserByEmail(email); 53 | // Assert 54 | assertNotNull(user); 55 | assertEquals(user.getEmail(), email); 56 | } 57 | 58 | @Test 59 | public void testGetUserByIdReturnsNull() { 60 | Integer nonExistId = 99; 61 | 62 | assertThrows(ResourceNotFoundException.class, () -> userService.getUserById(nonExistId)); 63 | } 64 | 65 | @Test 66 | public void testGetUserByUsernameReturnsNull() { 67 | String nonExistUsername = "null"; 68 | 69 | assertThrows(ResourceNotFoundException.class, () -> userService.getUserByUsername(nonExistUsername)); 70 | } 71 | 72 | @Test 73 | public void testGetUserByEmailReturnsNull() { 74 | String nonExistEmail = "null"; 75 | 76 | assertThrows(ResourceNotFoundException.class, () -> userService.getUserByEmail(nonExistEmail)); 77 | } 78 | 79 | } 80 | 81 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/repository/AuctionHistoryRepository.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.repository; 2 | 3 | import org.springframework.data.domain.Page; 4 | import org.springframework.data.domain.Pageable; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | import org.springframework.data.jpa.repository.Query; 7 | import org.springframework.data.repository.query.Param; 8 | import vn.webapp.backend.auction.enums.AuctionHistoryState; 9 | import vn.webapp.backend.auction.model.AuctionHistory; 10 | 11 | import java.util.List; 12 | 13 | public interface AuctionHistoryRepository extends JpaRepository { 14 | @Query("SELECT ah FROM AuctionHistory ah WHERE ah.auction.id = :id AND ah.state = 'ACTIVE'") 15 | Page findByAuctionId(Pageable pageable, @Param("id") Integer id); 16 | 17 | @Query("SELECT ah FROM AuctionHistory ah WHERE ah.user.username = :username") 18 | Page findByUsername(Pageable pageable, @Param("username") String username); 19 | 20 | @Query(value = "SELECT ah FROM auction_history ah WHERE CAST(ah.time AS DATE) = :date", nativeQuery = true) 21 | List findByDate(@Param("date") String date); 22 | 23 | @Query("SELECT ah FROM AuctionHistory ah WHERE ah.auction.id = :id") 24 | List findByAuctionIdWhenFinished(@Param("id") Integer id); 25 | 26 | @Query("SELECT COUNT(ah) FROM AuctionHistory ah WHERE ah.user.username = :username") 27 | Integer getTotalBidByUsername(@Param("username") String username); 28 | 29 | @Query("SELECT ah FROM AuctionHistory ah WHERE ah.auction.id = :auctionId AND ah.user.id = :userId AND ah.state = 'ACTIVE'") 30 | List findByAuctionHistoryByAuctionAndUserActive(@Param("auctionId") Integer auctionId, @Param("userId") Integer userId); 31 | 32 | @Query("SELECT ah FROM AuctionHistory ah WHERE ah.auction.id = :auctionId AND ah.state != 'HIDDEN' ORDER BY ah.time DESC") 33 | List findLastActiveBidByAuctionId(@Param("auctionId") Integer auctionId); 34 | 35 | @Query("SELECT ah FROM AuctionHistory ah WHERE ah.auction.id = :auctionId AND ah.state != 'HIDDEN' ORDER BY ah.priceGiven DESC") 36 | List findTopBidByAuctionId(@Param("auctionId") Integer auctionId); 37 | 38 | @Query("SELECT ah FROM AuctionHistory ah WHERE ah.auction.id = :auctionId AND ah.user.id = :userId AND ah.state = :state") 39 | Page findByAuctionIdAndUserId(@Param("auctionId") Integer auctionId, @Param("userId") Integer userId,@Param("state") AuctionHistoryState state, Pageable pageable); 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/vn/webapp/backend/auction/JewelryCategoryServiceTest.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 7 | import vn.webapp.backend.auction.exception.ResourceNotFoundException; 8 | import vn.webapp.backend.auction.model.JewelryCategory; 9 | import vn.webapp.backend.auction.service.category.JewelryCategoryService; 10 | 11 | import java.util.List; 12 | 13 | import static org.testng.Assert.*; 14 | 15 | @SpringBootTest 16 | public class JewelryCategoryServiceTest extends AbstractTestNGSpringContextTests{ 17 | @Autowired 18 | private JewelryCategoryService jewelryCategoryService; 19 | 20 | @Test 21 | public void testGetAllReturnsWell(){ 22 | //Act 23 | List jewelries = jewelryCategoryService.getAll(); 24 | 25 | //Assert 26 | assertNotNull(jewelries); 27 | assertFalse(jewelries.isEmpty()); 28 | } 29 | 30 | @Test 31 | public void testGetJewelryCategoryByIdReturnsWell(){ 32 | // Expected 33 | Integer id = 1; 34 | 35 | // Act 36 | JewelryCategory jewelryCategory = jewelryCategoryService.getById(id); 37 | 38 | // Assert 39 | assertNotNull(jewelryCategory); 40 | assertEquals(jewelryCategory.getId(), id); 41 | } 42 | 43 | @Test 44 | public void testGetJewelryCategoryByIdReturnNull(){ 45 | // Expected 46 | Integer nonExistId = 99; 47 | 48 | //Act and Assert 49 | assertThrows(ResourceNotFoundException.class, () -> jewelryCategoryService.getById(nonExistId)); 50 | } 51 | 52 | // @Test 53 | // public void testSaveJewelryCategory(){ 54 | // // Expected 55 | // JewelryCategory jewelryCategoryToSave = JewelryCategory.builder().name("Dây chuyền").build(); 56 | // JewelryCategory savedJewelryCategory = JewelryCategory.builder() 57 | // .id(1) 58 | // .name("Dây chuyền") 59 | // .build(); 60 | // //Act 61 | // JewelryCategory result = jewelryCategoryService.saveJewelryCategory(jewelryCategoryToSave); 62 | // 63 | // // Assert 64 | // assertNotNull(result); 65 | // assertEquals(savedJewelryCategory.getId(), result.getId()); 66 | // assertEquals(savedJewelryCategory.getName(), result.getName()); 67 | // 68 | //// verify(jewelryCategoryRepository, times(1)).save(any(JewelryCategory.class)); 69 | // } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/model/Auction.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.model; 2 | 3 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 4 | import jakarta.persistence.*; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | import vn.webapp.backend.auction.enums.AuctionState; 10 | 11 | import java.sql.Timestamp; 12 | import java.time.Duration; 13 | 14 | @Entity 15 | @Data 16 | @AllArgsConstructor 17 | @NoArgsConstructor 18 | @Builder 19 | @Table(name = "auction") 20 | public class Auction { 21 | 22 | @Id 23 | @GeneratedValue(strategy = GenerationType.IDENTITY) 24 | private int id; 25 | 26 | @Column(name = "name", nullable = false) 27 | private String name; 28 | 29 | @Column(name = "description", nullable = false) 30 | private String description; 31 | 32 | @Column(name = "first_price", nullable = false) 33 | private double firstPrice; 34 | 35 | @Column(name = "last_price", nullable = true) 36 | private Double lastPrice; 37 | 38 | @Column(name = "participation_fee", nullable = false) 39 | private double participationFee; 40 | 41 | @Column(name = "deposit", nullable = false) 42 | private double deposit; 43 | 44 | @Column(name = "priceStep", nullable = false) 45 | private double priceStep; 46 | 47 | @Column(name = "create_date", nullable = true) 48 | private Timestamp createDate; 49 | 50 | @Column(name = "start_date", nullable = false) 51 | private Timestamp startDate; 52 | 53 | @Column(name = "end_date", nullable = false) 54 | private Timestamp endDate; 55 | 56 | @Column(name = "end_date_stored") 57 | private Timestamp endDateStored; 58 | 59 | @Transient 60 | private long countdownDuration; 61 | 62 | @ManyToOne(cascade = { 63 | CascadeType.PERSIST, CascadeType.DETACH, 64 | CascadeType.MERGE, CascadeType.REFRESH 65 | }) 66 | @JoinColumn(name = "staff_id") 67 | private User user; 68 | 69 | @ManyToOne(cascade = { 70 | CascadeType.PERSIST, CascadeType.DETACH, 71 | CascadeType.MERGE, CascadeType.REFRESH 72 | }) 73 | @JoinColumn(name = "jewelry_id") 74 | private Jewelry jewelry; 75 | 76 | @Enumerated(EnumType.STRING) 77 | private AuctionState state; 78 | 79 | public long getCountdownDuration() { 80 | if (startDate != null && endDate != null) { 81 | Duration duration = Duration.between(startDate.toInstant(), endDate.toInstant()); 82 | return duration.toMillis(); 83 | } 84 | return 0; 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/repository/AuctionRegistrationRepository.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.repository; 2 | 3 | import org.springframework.data.domain.Page; 4 | import org.springframework.data.domain.Pageable; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | import org.springframework.data.jpa.repository.Query; 7 | import org.springframework.data.repository.query.Param; 8 | import vn.webapp.backend.auction.model.AuctionRegistration; 9 | 10 | import java.util.List; 11 | import java.util.Optional; 12 | 13 | public interface AuctionRegistrationRepository extends JpaRepository { 14 | 15 | @Query("SELECT ar FROM AuctionRegistration ar JOIN FETCH ar.auction a WHERE a.id = :auctionId") 16 | List findByAuctionIdAndValid(@Param("auctionId") Integer auctionId); 17 | 18 | @Query("SELECT ar FROM AuctionRegistration ar WHERE ar.auction.id = :auctionId AND ar.user.id = :userId AND ar.auctionRegistrationState = 'VALID'") 19 | Optional findByAuctionIdAndUserIdValid(@Param("userId") Integer userId, @Param("auctionId") Integer auctionId); 20 | 21 | @Query("SELECT ar FROM AuctionRegistration ar WHERE ar.auction.id = :auctionId AND ar.user.id = :userId AND ar.auctionRegistrationState = 'KICKED_OUT'") 22 | Optional findByAuctionIdAndUserIdInValid(@Param("userId") Integer userId, @Param("auctionId") Integer auctionId); 23 | 24 | @Query("SELECT ar FROM AuctionRegistration ar JOIN FETCH ar.user a WHERE a.id = :userId AND (:auctionName IS NULL OR ar.auction.name LIKE %:auctionName%)") 25 | Page findByUserIdAndValid(@Param("userId") Integer userId,@Param("auctionName") String auctionName, Pageable pageable); 26 | 27 | @Query("SELECT ar FROM AuctionRegistration ar WHERE ar.user.id = :userId AND ar.auctionRegistrationState = 'VALID'") 28 | List findByUserIdValid(@Param("userId") Integer userId); 29 | 30 | @Query("SELECT SUM(ar.registrationFee) FROM AuctionRegistration ar") 31 | Double sumTotalRegistrationFee(); 32 | 33 | @Query("SELECT COUNT(DISTINCT ar.user.id) FROM AuctionRegistration ar WHERE ar.auctionRegistrationState = 'VALID' AND MONTH(ar.registrationDate) = :month AND YEAR(ar.registrationDate) = :year") 34 | Long countDistinctUsersRegistered(@Param("month") Integer month, @Param("year") Integer year); 35 | 36 | @Query("SELECT COUNT(ar) FROM AuctionRegistration ar WHERE ar.auction.id = :auctionId AND ar.auctionRegistrationState = 'VALID'") 37 | Integer countValidParticipantsByAuctionId(@Param("auctionId") Integer auctionId); 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/vn/webapp/backend/auction/TransactionServiceTest.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import vn.webapp.backend.auction.exception.ResourceNotFoundException; 7 | import vn.webapp.backend.auction.model.Transaction; 8 | import vn.webapp.backend.auction.service.transaction.TransactionService; 9 | 10 | import java.util.List; 11 | 12 | import static org.testng.Assert.*; 13 | 14 | @SpringBootTest 15 | public class TransactionServiceTest { 16 | @Autowired 17 | private TransactionService transactionService; 18 | 19 | @Test 20 | public void testGetAllReturnsWell(){ 21 | // Act 22 | List transactions = transactionService.getAll(); 23 | 24 | // Assert 25 | assertNotNull(transactions); 26 | assertFalse(transactions.isEmpty()); 27 | } 28 | @Test 29 | public void testGetTransactionByIdReturnsWell(){ 30 | // Expected 31 | Integer id = 1; 32 | 33 | // Act 34 | Transaction transaction = transactionService.getTransactionById(id); 35 | 36 | // Assert 37 | assertNotNull(transaction); 38 | assertEquals(transaction.getId(),id); 39 | } 40 | 41 | @Test 42 | public void testGetTransactionByIdReturnsNull(){ 43 | // Expected 44 | Integer nonExistId = 99; 45 | 46 | // Act and Assert 47 | assertThrows(ResourceNotFoundException.class, () -> transactionService.getTransactionById(nonExistId)); 48 | } 49 | 50 | // @Test //>>>CHECK LẠI UserTransactionResponse<<< 51 | // public void testGetTransactionsDashboardByUsername(){ 52 | // //Expected 53 | // String username = "phuuthanh2003"; 54 | // 55 | // //Act 56 | // UserTransactionResponse userTransactionResponse = transactionService.getTransactionsDashboardByUsername(username); 57 | // 58 | // //Assert 59 | // assertNotNull(userTransactionResponse); 60 | // assertTrue(username, userTransactionResponse.); 61 | // } 62 | 63 | // @Test // >>>CHECK LẠI transaction.getType()<<< 64 | // public void testGetTransactionByType(){ 65 | // //Expected 66 | // String typename = "Type đếu biết"; 67 | // 68 | // //Act 69 | // List transactions = transactionService.getTransactionByType(typename); 70 | // 71 | // //Assert 72 | // assertNotNull(transactions); 73 | // assertFalse(transactions.isEmpty()); 74 | // 75 | // for (Transaction transaction : transactions){ 76 | // assertTrue(transaction.getType().toLowerCase().contains(typename.toLowerCase())); 77 | // } 78 | // } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/service/realtime/RealTimeService.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.service.realtime; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.stereotype.Service; 5 | import vn.webapp.backend.auction.dto.BidResponse; 6 | import vn.webapp.backend.auction.dto.KickOutResponse; 7 | import vn.webapp.backend.auction.model.Auction; 8 | import vn.webapp.backend.auction.model.AuctionRegistration; 9 | import vn.webapp.backend.auction.model.ErrorMessages; 10 | import vn.webapp.backend.auction.model.User; 11 | import vn.webapp.backend.auction.repository.AuctionRegistrationRepository; 12 | import vn.webapp.backend.auction.repository.AuctionRepository; 13 | import vn.webapp.backend.auction.repository.UserRepository; 14 | 15 | import java.sql.Timestamp; 16 | import java.util.Optional; 17 | 18 | @Service 19 | @RequiredArgsConstructor 20 | public class RealTimeService { 21 | 22 | private final AuctionRepository auctionRepository; 23 | private final UserRepository userRepository; 24 | private final AuctionRegistrationRepository auctionRegistrationRepository; 25 | 26 | public BidResponse bidRealtime(Integer id, Long bonusTime, String username) { 27 | Auction auction = auctionRepository.findById(id) 28 | .orElseThrow(() -> new IllegalArgumentException(ErrorMessages.AUCTION_NOT_FOUND)); 29 | User user = userRepository.findByUsername(username) 30 | .orElseThrow(() -> new IllegalArgumentException(ErrorMessages.USER_NOT_FOUND)); 31 | 32 | Timestamp currentEndDate = auction.getEndDate(); 33 | Timestamp newEndDate = new Timestamp(currentEndDate.getTime() + bonusTime); 34 | auction.setEndDate(newEndDate); 35 | auctionRepository.save(auction); 36 | 37 | return new BidResponse(auction.getLastPrice(), auction.getJewelry().getBuyNowPrice(), auction.getId(), 38 | auction.getEndDate(), bonusTime, user.getUsername()); 39 | } 40 | 41 | public KickOutResponse staffKickOutMemberRealtime(Integer id, String username) { 42 | Auction auction = auctionRepository.findById(id) 43 | .orElseThrow(() -> new IllegalArgumentException(ErrorMessages.AUCTION_NOT_FOUND)); 44 | User user = userRepository.findByUsername(username) 45 | .orElseThrow(() -> new IllegalArgumentException(ErrorMessages.USER_NOT_FOUND)); 46 | AuctionRegistration auctionRegistration = auctionRegistrationRepository.findByAuctionIdAndUserIdInValid(user.getId(), auction.getId()) 47 | .orElseThrow(() -> new IllegalArgumentException(ErrorMessages.AUCTION_REGISTRATION_NOT_FOUND)); 48 | return new KickOutResponse(auctionRegistration.getUser().getId(), auction.getLastPrice(), auctionRegistration.getKickReason(), auction.getEndDate()); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/model/Jewelry.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import jakarta.persistence.*; 5 | import jakarta.validation.constraints.Min; 6 | import jakarta.validation.constraints.NotBlank; 7 | import lombok.*; 8 | import vn.webapp.backend.auction.enums.JewelryMaterial; 9 | import vn.webapp.backend.auction.enums.JewelryState; 10 | 11 | import java.sql.Timestamp; 12 | import java.util.List; 13 | 14 | @Entity 15 | @Data 16 | @Builder 17 | @AllArgsConstructor 18 | @NoArgsConstructor 19 | @Table(name = "jewelry") 20 | public class Jewelry { 21 | 22 | @Id 23 | @GeneratedValue(strategy = GenerationType.IDENTITY) 24 | private int id; 25 | 26 | @Column(name = "name", nullable = false) 27 | @NotBlank(message = "The price required") 28 | private String name; 29 | 30 | @Column(name = "buy_now_price") 31 | @Min(value = 1, message = "The price must be at least 1") 32 | private Double buyNowPrice; 33 | 34 | @Column(name = "description", nullable = false) 35 | private String description; 36 | 37 | @Enumerated(EnumType.STRING) 38 | private JewelryMaterial material; 39 | 40 | @Column(name = "brand", nullable = false) 41 | private String brand; 42 | 43 | @Column(name = "weight", nullable = false) 44 | private Double weight; 45 | 46 | @Column(name = "is_holding") 47 | private Boolean isHolding; 48 | 49 | @Column(name = "received_date") 50 | private Timestamp receivedDate; 51 | 52 | @Column(name = "delivery_date") 53 | private Timestamp deliveryDate; 54 | 55 | @Column(name = "create_date") 56 | private Timestamp createDate; 57 | 58 | @Column(name = "state", nullable = false) 59 | @Enumerated(EnumType.STRING) 60 | private JewelryState state; 61 | 62 | @OneToMany(mappedBy = "jewelry", cascade = CascadeType.ALL) 63 | @EqualsAndHashCode.Exclude 64 | @ToString.Exclude 65 | @JsonIgnore 66 | private List images; 67 | 68 | @ManyToOne(cascade = { 69 | CascadeType.PERSIST, CascadeType.DETACH, 70 | CascadeType.MERGE, CascadeType.REFRESH 71 | }) 72 | @JoinColumn(name = "user_id") 73 | @EqualsAndHashCode.Exclude 74 | @ToString.Exclude 75 | private User user; 76 | 77 | @ManyToOne(cascade = { 78 | CascadeType.PERSIST, CascadeType.DETACH, 79 | CascadeType.MERGE, CascadeType.REFRESH 80 | }) 81 | @JoinColumn(name = "category_id") 82 | @EqualsAndHashCode.Exclude 83 | @ToString.Exclude 84 | private JewelryCategory category; 85 | 86 | @OneToMany(mappedBy = "jewelry", cascade = CascadeType.ALL) 87 | @EqualsAndHashCode.Exclude 88 | @ToString.Exclude 89 | @JsonIgnore 90 | private List requestApprovals; 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/repository/RequestApprovalRepository.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.repository; 2 | 3 | import org.springframework.data.domain.Page; 4 | import org.springframework.data.domain.Pageable; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | import org.springframework.data.jpa.repository.Query; 7 | import org.springframework.data.repository.query.Param; 8 | import vn.webapp.backend.auction.enums.Role; 9 | import vn.webapp.backend.auction.model.RequestApproval; 10 | 11 | 12 | public interface RequestApprovalRepository extends JpaRepository { 13 | @Query("SELECT ra FROM RequestApproval ra WHERE ra.sender.role = :role " + 14 | "AND (:jewelryName IS NULL OR ra.jewelry.name LIKE %:jewelryName%) " + 15 | "AND (:category IS NULL OR ra.jewelry.category.name = :category) " + 16 | "AND ra.jewelry.state = 'APPROVING' " + 17 | "AND ra.isConfirm = false AND ra.state = 'ACTIVE'") 18 | Page findRequestApprovalBySenderRole( 19 | @Param("role") Role role, 20 | @Param("jewelryName") String jewelryName, 21 | @Param("category") String category, 22 | Pageable pageable 23 | ); 24 | 25 | @Query("SELECT ra FROM RequestApproval ra WHERE ra.sender.role = 'MANAGER'" + 26 | "AND ra.isConfirm = false AND ra.jewelry.user.id = :memberId " + 27 | "AND ra.state = 'ACTIVE' AND (:jewelryName IS NULL OR ra.jewelry.name LIKE %:jewelryName%) ") 28 | Page findRequestNeedConfirmByMember( 29 | @Param("memberId") Integer memberId, 30 | @Param("jewelryName") String jewelryName, 31 | Pageable pageable); 32 | 33 | @Query("SELECT ra FROM RequestApproval ra WHERE ra.sender.id = :id " + 34 | "AND (:jewelryName IS NULL OR ra.jewelry.name LIKE %:jewelryName%) " + 35 | "AND (:category IS NULL OR ra.jewelry.category.name = :category)" + 36 | "ORDER BY ra.isConfirm ASC") 37 | Page findRequestApprovalByUserId(@Param("id") Integer id, @Param("jewelryName") String jewelryName,@Param("category") String category, Pageable pageable); 38 | 39 | @Query("SELECT ra FROM RequestApproval ra " + 40 | "WHERE " + 41 | "ra.sender.role = 'MANAGER' AND " + 42 | "(:jewelryName IS NULL OR ra.jewelry.name LIKE %:jewelryName%) " + 43 | "AND (:category IS NULL OR ra.jewelry.category.name = :category) " + 44 | "AND ra.isConfirm = true AND ra.state = 'ACTIVE' " + 45 | "AND ra.jewelry.state = 'ACTIVE' AND ra.jewelry.isHolding = true") 46 | Page findRequestApprovalPassed( 47 | @Param("jewelryName") String jewelryName, 48 | @Param("category") String category, 49 | Pageable pageable 50 | ); 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/service/vnpay/VNPayUtil.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.service.vnpay; 2 | import javax.crypto.Mac; 3 | import javax.crypto.spec.SecretKeySpec; 4 | import java.net.URLEncoder; 5 | import java.nio.charset.StandardCharsets; 6 | import java.util.Map; 7 | import java.util.Random; 8 | import java.util.stream.Collectors; 9 | import jakarta.servlet.http.HttpServletRequest; 10 | public class VNPayUtil { 11 | public static String hmacSHA512(final String key, final String data) { 12 | try { 13 | if (key == null || data == null) { 14 | throw new NullPointerException(); 15 | } 16 | final Mac hmac512 = Mac.getInstance("HmacSHA512"); 17 | byte[] hmacKeyBytes = key.getBytes(); 18 | final SecretKeySpec secretKey = new SecretKeySpec(hmacKeyBytes, "HmacSHA512"); 19 | hmac512.init(secretKey); 20 | byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8); 21 | byte[] result = hmac512.doFinal(dataBytes); 22 | StringBuilder sb = new StringBuilder(2 * result.length); 23 | for (byte b : result) { 24 | sb.append(String.format("%02x", b & 0xff)); 25 | } 26 | return sb.toString(); 27 | 28 | } catch (Exception ex) { 29 | return ""; 30 | } 31 | } 32 | 33 | public static String getIpAddress(HttpServletRequest request) { 34 | String ipAdress; 35 | try { 36 | ipAdress = request.getHeader("X-FORWARDED-FOR"); 37 | if (ipAdress == null) { 38 | ipAdress = request.getRemoteAddr(); 39 | } 40 | } catch (Exception e) { 41 | ipAdress = "Invalid IP:" + e.getMessage(); 42 | } 43 | return ipAdress; 44 | } 45 | 46 | public static String getRandomNumber(int len) { 47 | Random rnd = new Random(); 48 | String chars = "0123456789"; 49 | StringBuilder sb = new StringBuilder(len); 50 | for (int i = 0; i < len; i++) { 51 | sb.append(chars.charAt(rnd.nextInt(chars.length()))); 52 | } 53 | return sb.toString(); 54 | } 55 | public static String getPaymentURL(Map paramsMap, boolean encodeKey) { 56 | return paramsMap.entrySet().stream() 57 | .filter(entry -> entry.getValue() != null && !entry.getValue().isEmpty()) 58 | .sorted(Map.Entry.comparingByKey()) 59 | .map(entry -> 60 | (encodeKey ? URLEncoder.encode(entry.getKey(), 61 | StandardCharsets.US_ASCII) 62 | : entry.getKey()) + "=" + 63 | URLEncoder.encode(entry.getValue() 64 | , StandardCharsets.US_ASCII)) 65 | .collect(Collectors.joining("&")); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Spring Boot Project](https://github.com/phuuthanh2003/AuctionWebApp_BE/actions/workflows/build.yml/badge.svg)](https://github.com/phuuthanh2003/AuctionWebApp_BE/actions/workflows/build.yml) 2 | [![Release Auction REST API](https://github.com/phuuthanh2003/AuctionWebApp_BE/actions/workflows/release.yml/badge.svg)](https://github.com/phuuthanh2003/AuctionWebApp_BE/actions/workflows/release.yml) 3 | [![Publish Auction Backend Docker Image](https://github.com/phuuthanh2003/AuctionWebApp_BE/actions/workflows/docker-publish.yml/badge.svg)](https://github.com/phuuthanh2003/AuctionWebApp_BE/actions/workflows/docker-publish.yml) 4 | [![Deploy Spring Boot with Railway](https://github.com/phuuthanh2003/AuctionWebApp_BE/actions/workflows/deploy.yml/badge.svg)](https://github.com/phuuthanh2003/AuctionWebApp_BE/actions/workflows/deploy.yml) 5 | [![CodeFactor](https://www.codefactor.io/repository/github/phuuthanh-dev/jewelry-auction-be/badge)](https://www.codefactor.io/repository/github/phuuthanh-dev/jewelry-auction-be) 6 | ## Requirements 7 | 8 | - JDK 17 or later 9 | - SQL Server 2019 10 | - Maven 3+ 11 | 12 | ## How to Run 13 | - Clone the repository: 14 | ```bash 15 | git clone https://github.com/phuuthanh2003/AuctionWebApp_BE.git 16 | ``` 17 | - Open in your preferred IDE. 18 | 19 | - Navigate to the project directory: 20 | ```bash 21 | cd AuctionWebApp_BE 22 | ``` 23 | - You can build the project and run the tests by running: 24 | ```bash 25 | mvn clean package 26 | ``` 27 | - Once successfully built, you can run the service: 28 | ```bash 29 | mvn spring-boot:run 30 | ``` 31 | 32 | ### To view Swagger 3 API docs 33 | 34 | Run the server and browse to https://auction-webapp-production.up.railway.app/swagger-ui/index.html 35 | 36 | ## Contributors 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 |

Phùng Hữu Thành

Huy Hoang

Tâm

Automated code reviews

Railway
52 | 53 | ## License & Copyright 54 | © 2024 Phung Huu Thanh Licensed under the [Apache License 2.0](https://github.com/phuuthanh2003/AuctionWebApp_BE/blob/main/LICENSE). 55 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/exception/GlobalExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.exception; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.http.HttpStatus; 6 | import org.springframework.http.ResponseEntity; 7 | import org.springframework.web.bind.annotation.RestControllerAdvice; 8 | import vn.webapp.backend.auction.model.ErrorResponse; 9 | import org.springframework.web.bind.annotation.ExceptionHandler; 10 | 11 | @RestControllerAdvice 12 | public class GlobalExceptionHandler { 13 | private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); 14 | 15 | @ExceptionHandler(ResourceNotFoundException.class) 16 | public ResponseEntity handleResourceNotFoundException(ResourceNotFoundException ex) { 17 | ErrorResponse err = new ErrorResponse(HttpStatus.NOT_FOUND.value(), ex.getMessage()); 18 | return ResponseEntity.status(HttpStatus.NOT_FOUND).body(err); 19 | } 20 | 21 | @ExceptionHandler(AccountDisabledException.class) 22 | public ResponseEntity handleAccountDisabledException(AccountDisabledException ex) { 23 | ErrorResponse err = new ErrorResponse(HttpStatus.NOT_FOUND.value(), ex.getMessage()); 24 | return ResponseEntity.status(HttpStatus.NOT_FOUND).body(err); 25 | } 26 | 27 | @ExceptionHandler(AccountInactiveException.class) 28 | public ResponseEntity handleAccountInactiveException(AccountInactiveException ex) { 29 | ErrorResponse err = new ErrorResponse(HttpStatus.BAD_REQUEST.value(), ex.getMessage()); 30 | return ResponseEntity.status(202).body(err); 31 | } 32 | 33 | @ExceptionHandler(UserAlreadyExistsException.class) 34 | public ResponseEntity handleUserAlreadyExistsException(UserAlreadyExistsException ex) { 35 | ErrorResponse err = new ErrorResponse(HttpStatus.NOT_FOUND.value(), ex.getMessage()); 36 | return ResponseEntity.status(HttpStatus.NOT_FOUND).body(err); 37 | } 38 | 39 | @ExceptionHandler(OldPasswordMismatchException.class) 40 | public ResponseEntity handleOldPasswordMismatchException(OldPasswordMismatchException ex) { 41 | ErrorResponse err = new ErrorResponse(HttpStatus.NOT_FOUND.value(), ex.getMessage()); 42 | return ResponseEntity.status(HttpStatus.NOT_FOUND).body(err); 43 | } 44 | 45 | @ExceptionHandler(UserNotFoundException.class) 46 | public ResponseEntity handleUserNotFoundException(UserNotFoundException ex) { 47 | ErrorResponse err = new ErrorResponse(HttpStatus.NOT_FOUND.value(), ex.getMessage()); 48 | return ResponseEntity.status(HttpStatus.NOT_FOUND).body(err); 49 | } 50 | 51 | @ExceptionHandler({UnauthorizedException.class, UserNotAllowedAccess.class}) 52 | public ResponseEntity handleUnauthorizedException(UnauthorizedException ex) { 53 | ErrorResponse err = new ErrorResponse(HttpStatus.FORBIDDEN.value(), ex.getMessage()); 54 | return ResponseEntity.status(HttpStatus.FORBIDDEN).body(err); 55 | } 56 | 57 | @ExceptionHandler 58 | public ResponseEntity handleUnwantedException(Exception ex) { 59 | ErrorResponse err = new ErrorResponse(HttpStatus.BAD_REQUEST.value(), ex.getMessage()); 60 | return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(err); 61 | } 62 | } 63 | 64 | 65 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/security/Endpoints.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.security; 2 | 3 | public final class Endpoints { 4 | 5 | private Endpoints() { 6 | throw new UnsupportedOperationException("Utility class"); 7 | } 8 | 9 | public static final String[] PUBLIC_GET_ENDPOINTS = { 10 | "/api/v1/jewelry/**", "/api/v1/jewelry-category/**", "/api/v1/auction/**", 11 | "/api/v1/auction-history/get-by-auction/**", "/api/v1/auction-history/get-by-username/**", 12 | "/api/v1/auction-history/get-by-date/**", "/api/v1/image/**", "/api/v1/user/by-email/**", 13 | "/api/v1/user/by-username/**", "/api/v1/bank/**", "/api/v1/payment/**", 14 | "/api/v1/auction-registration/**", "/api/v1/transaction/**", "api/v1/bank", 15 | "/api/v1/auction-history/get-when-auction-finished/**", 16 | "/api/v1/user/get-winner-auction/**", "/api/v1/request-approval/**", 17 | "/api/v1/transaction/get-by-type-state", "/api/v1/auction-history/get-by-auction-and-user/**" 18 | }; 19 | 20 | public static final String[] PUBLIC_POST_ENDPOINTS = { 21 | "/api/v1/auction-history", "/api/v1/jewelry/jewelry-request","/api/v1/image/add-image", 22 | "/api/v1/request-approval/send-from-user", "/api/v1/request-approval/send-from-staff", "/api/v1/request-approval/send-from-manager", 23 | "/api/v1/transaction/create-transaction-for-winner/**", "/api/v1/transaction/create-transaction-for-winner-if-not-exist/**", 24 | "/api/v1/transaction/create-transaction-for-seller/**" 25 | }; 26 | 27 | public static final String[] PUBLIC_PUT_ENDPOINTS = { 28 | "/api/v1/user/change-password", "/api/v1/auction/update-end-date/**", 29 | "/api/v1/auction/set-state/**", "/api/v1/user","/api/v1/request-approval/set-state/**", 30 | "/api/v1/request-approval/confirm/**", "/api/v1/request-approval/cancel-request", 31 | "/api/v1/transaction/set-method/**", "/api/v1/jewelry/set-state-holding/**" 32 | }; 33 | 34 | public static final String[] STAFF_GET_ENDPOINTS = { 35 | "/api/v1/user/get-user-registration/**", 36 | }; 37 | 38 | public static final String[] MANAGER_GET_ENDPOINTS = { 39 | "/api/v1/transaction/get-handover", "/api/v1/jewelry/manager-list" 40 | }; 41 | 42 | public static final String[] MANAGER_PUT_ENDPOINTS = { 43 | "/api/v1/jewelry/**", "/api/v1/jewelry-category/**", "/api/v1/auction/**", 44 | "/api/v1/transaction/set-state/**" 45 | }; 46 | 47 | public static final String[] MANAGER_POST_ENDPOINTS = { 48 | "/api/v1/jewelry/**", "/api/v1/jewelry-category/**", "/api/v1/auction/**" 49 | }; 50 | 51 | public static final String[] MANAGER_DELETE_ENDPOINTS = { 52 | "/api/v1/jewelry/**", "/api/v1/jewelry-category/**", "/api/v1/auction/**", 53 | "/api/v1/image/jewelry/**" 54 | }; 55 | 56 | 57 | public static final String[] MANAGER_ADMIN_GET_ENDPOINTS = { 58 | "/api/v1/user/**" 59 | }; 60 | 61 | public static final String[] ADMIN_GET_ENDPOINTS = { 62 | // "/api/v1/user/**" 63 | }; 64 | 65 | public static final String[] ADMIN_POST_ENDPOINTS = { 66 | "/api/v1/user/**" 67 | }; 68 | 69 | public static final String[] ADMIN_PUT_ENDPOINTS = { 70 | "/api/v1/user/**" 71 | }; 72 | } 73 | -------------------------------------------------------------------------------- /src/test/java/vn/webapp/backend/auction/JewelryServiceTest.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.ActiveProfiles; 7 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 8 | import vn.webapp.backend.auction.exception.ResourceNotFoundException; 9 | 10 | import vn.webapp.backend.auction.model.Jewelry; 11 | import vn.webapp.backend.auction.service.jewelry.JewelryService; 12 | 13 | import java.util.List; 14 | 15 | import static org.testng.Assert.*; 16 | 17 | @SpringBootTest 18 | @ActiveProfiles("test") 19 | public class JewelryServiceTest extends AbstractTestNGSpringContextTests { 20 | 21 | @Autowired 22 | private JewelryService jewelryService; 23 | 24 | @Test 25 | public void testGetJewelryByIdReturnsWell(){ 26 | // Expected 27 | Integer id = 1; 28 | 29 | // Act 30 | Jewelry jewelry = jewelryService.getJewelryById(id); 31 | 32 | // Assert 33 | assertNotNull(jewelry); 34 | assertEquals(jewelry.getId(), id); 35 | } 36 | 37 | @Test 38 | public void testGetJewelriesByUsernameReturnsWell(){ 39 | // Expected 40 | String username = "phuuthanh2003"; 41 | // Act 42 | List jewelries = jewelryService.getJewelryByUsername(username); 43 | // Assert 44 | assertNotNull(jewelries); 45 | assertFalse(jewelries.isEmpty()); 46 | // Verify that each returned Jewelry item belongs to the expected username 47 | for (Jewelry jewelry : jewelries) { 48 | assertEquals(jewelry.getUser().getUsername(), username); 49 | } 50 | } 51 | 52 | @Test 53 | public void TestGetJeweriesByCategoryIdReturnsWell(){ 54 | // Expected 55 | Integer id = 1; 56 | // Act 57 | List jewelries = jewelryService.getJewelriesByCategoryId(id); 58 | // Assert 59 | assertNotNull(jewelries); 60 | assertFalse(jewelries.isEmpty()); 61 | // Verify that each returned Jewelry item belongs to the expected username 62 | for (Jewelry jewelry : jewelries) { 63 | assertEquals(jewelry.getCategory().getId(), id); 64 | } 65 | } 66 | 67 | @Test 68 | public void testGetJewelriesByNameContainReturnsWell(){ 69 | // Expected 70 | String key = "Nhẫn"; 71 | // Act 72 | List jewelries = jewelryService.getJewelriesByNameContain(key); 73 | // Assert 74 | assertNotNull(jewelries); 75 | assertFalse(jewelries.isEmpty()); 76 | // Verify that each returned Jewelry item belongs to the expected username 77 | for (Jewelry jewelry : jewelries) { 78 | assertTrue(jewelry.getDescription().toLowerCase().contains(key.toLowerCase())); 79 | } 80 | } 81 | 82 | @Test 83 | public void testGetJewelryByIdReturnsNull(){ 84 | Integer nonExistId = 99; 85 | 86 | assertThrows(ResourceNotFoundException.class, () -> jewelryService.getJewelryById(nonExistId)); 87 | } 88 | 89 | // @Test 90 | // public void testGetJewelriesByUsernameReturnNull(){ 91 | // String nonExistUsername = "tam12345"; 92 | // assertThrows(ResourceNotFoundException.class, () -> jewelryService.getJewelriesByUsername(nonExistUsername)); 93 | //// } 94 | // @Test 95 | // public void TestGetJeweriesByCategoryIdReturnsNulll(){ 96 | // 97 | // } 98 | // @Test 99 | // public void testGetJewelriesByNameContainReturnsNull(){ 100 | // 101 | // } 102 | 103 | } 104 | 105 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/controller/VNPAYController.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.controller; 2 | 3 | import jakarta.servlet.http.HttpServletRequest; 4 | import jakarta.servlet.http.HttpServletResponse; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.http.HttpStatus; 7 | import org.springframework.web.bind.annotation.*; 8 | import vn.webapp.backend.auction.config.frontend.FrontendConfiguration; 9 | import vn.webapp.backend.auction.dto.PaymentResponse; 10 | import vn.webapp.backend.auction.service.auction_registration.AuctionRegistrationService; 11 | import vn.webapp.backend.auction.service.payment.PaymentService; 12 | import vn.webapp.backend.auction.service.transaction.TransactionService; 13 | import vn.webapp.backend.auction.service.vnpay.ResponseObject; 14 | 15 | import java.io.IOException; 16 | import java.util.Map; 17 | 18 | @RestController 19 | @CrossOrigin(origins = {"http://localhost:3000", "https://fe-deploy-hazel.vercel.app"}) 20 | @RequestMapping("/api/v1/payment") 21 | @RequiredArgsConstructor 22 | public class VNPAYController { 23 | 24 | private final FrontendConfiguration frontendConfiguration; 25 | private final PaymentService paymentService; 26 | private final AuctionRegistrationService auctionRegistrationService; 27 | private final TransactionService transactionService; 28 | 29 | @GetMapping("/vn-pay") 30 | public ResponseObject pay(HttpServletRequest request) { 31 | return new ResponseObject<>(HttpStatus.OK, "Success", paymentService.createVnPayPayment(request)); 32 | } 33 | 34 | @GetMapping("/vn-pay-callback") 35 | public void payCallbackHandler( 36 | HttpServletRequest request, 37 | HttpServletResponse response, 38 | @RequestParam("username") String username, 39 | @RequestParam("auctionId") Integer auctionId, 40 | @RequestParam("transactionId") Integer transactionId 41 | ) throws IOException { 42 | String bankCode = request.getParameter("vnp_BankCode"); 43 | String status = request.getParameter("vnp_ResponseCode"); 44 | String transactionCode = request.getParameter("vnp_TransactionNo"); 45 | String redirectUrl = ""; 46 | 47 | if (transactionId == 0) { 48 | redirectUrl = handleAuctionPaymentCallback(username, auctionId, status, transactionCode, bankCode); 49 | } else { 50 | redirectUrl = handleTransactionPaymentCallback(transactionId, status, transactionCode, bankCode); 51 | } 52 | response.sendRedirect(redirectUrl); 53 | } 54 | 55 | private String handleAuctionPaymentCallback(String username, Integer auctionId, String status, String transactionCode, String bankCode) { 56 | String baseUrl = frontendConfiguration.getBaseUrl() + "/tai-san-dau-gia/"; 57 | String redirectUrl = baseUrl + auctionId; 58 | 59 | if (!status.equals("00")) { 60 | redirectUrl += "?paymentStatus=failed"; 61 | } else { 62 | auctionRegistrationService.registerUserForAuction(username, auctionId, transactionCode, bankCode); 63 | redirectUrl += "?paymentStatus=success"; 64 | } 65 | 66 | return redirectUrl; 67 | } 68 | 69 | private String handleTransactionPaymentCallback(Integer transactionId, String status, String transactionCode, String bankCode) { 70 | String redirectUrl = frontendConfiguration.getBaseUrl() + "/thong-tin-ca-nhan/"; 71 | 72 | if (!status.equals("00")) { 73 | redirectUrl += "?paymentStatus=failed"; 74 | } else { 75 | transactionService.setTransactionAfterPaySuccess(transactionId, transactionCode, bankCode); 76 | redirectUrl += "?paymentStatus=success"; 77 | } 78 | 79 | return redirectUrl; 80 | } 81 | 82 | } -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/controller/AuthenticationController.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.controller; 2 | 3 | import jakarta.mail.MessagingException; 4 | import jakarta.servlet.http.HttpServletRequest; 5 | import jakarta.servlet.http.HttpServletResponse; 6 | import lombok.RequiredArgsConstructor; 7 | import org.springframework.http.ResponseEntity; 8 | import org.springframework.web.bind.annotation.*; 9 | import vn.webapp.backend.auction.dto.*; 10 | import vn.webapp.backend.auction.dto.RegisterAccountRequest; 11 | import vn.webapp.backend.auction.service.authenticate.AuthenticationServiceImpl; 12 | 13 | import java.io.IOException; 14 | 15 | @RestController 16 | @RequestMapping("/api/v1/auth") 17 | @RequiredArgsConstructor 18 | @CrossOrigin(origins = {"http://localhost:3000", "http://localhost:3001"}) 19 | public class AuthenticationController { 20 | 21 | private final AuthenticationServiceImpl authenticationService; 22 | 23 | @PostMapping("/authenticate") 24 | public ResponseEntity authenticateGeneral( 25 | @RequestBody AuthenticationRequest request, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws MessagingException { 26 | 27 | AuthenticationResponse authenticationResponse = authenticationService.authenticateGeneral(request, httpServletRequest, httpServletResponse); 28 | 29 | if (authenticationResponse.getAccessToken() == null) { 30 | return ResponseEntity.status(403).body(authenticationResponse); 31 | } 32 | 33 | return ResponseEntity.ok().body(authenticationResponse); 34 | } 35 | 36 | @PostMapping("/authenticate-admin-manager") 37 | public ResponseEntity authenticateAdminManager( 38 | @RequestBody AuthenticationRequest request, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws MessagingException { 39 | 40 | AuthenticationResponse authenticationResponse = authenticationService.authenticateAdminManager(request, httpServletRequest, httpServletResponse); 41 | return ResponseEntity.ok().body(authenticationResponse); 42 | } 43 | 44 | @PostMapping("/change-password") 45 | public ResponseEntity changePassword( 46 | @RequestBody ChangePasswordRequest request) { 47 | authenticationService.changePassword(request); 48 | return ResponseEntity.ok().build(); 49 | } 50 | 51 | @PostMapping("/activation") 52 | public ResponseEntity activateAccount( 53 | @RequestBody ActivateAccountRequest request) throws MessagingException { 54 | authenticationService.activateAccount(request); 55 | return ResponseEntity.ok().build(); 56 | } 57 | 58 | @PostMapping("/register") 59 | public ResponseEntity register( 60 | @RequestBody RegisterAccountRequest request, HttpServletRequest httpServletRequest) throws MessagingException { 61 | return ResponseEntity.ok(authenticationService.register(request, httpServletRequest)); 62 | } 63 | 64 | @PostMapping("/refresh-token") 65 | public void refreshToken( 66 | HttpServletRequest request, 67 | HttpServletResponse response 68 | ) throws IOException { 69 | authenticationService.refreshToken(request, response); 70 | } 71 | 72 | @PostMapping("/forgot-password") 73 | public ResponseEntity forgotPassword( 74 | @RequestBody ForgotPasswordRequest request) throws MessagingException { 75 | authenticationService.forgotPassword(request); 76 | return ResponseEntity.ok().build(); 77 | } 78 | 79 | @PostMapping("/reset-password") 80 | public ResponseEntity resetPassword( 81 | @RequestBody ResetPasswordRequest request) { 82 | return ResponseEntity.ok(authenticationService.resetPassword(request)); 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/filter/JwtAuthenticationFilter.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.filter; 2 | 3 | import java.io.IOException; 4 | import java.util.Optional; 5 | 6 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 7 | import org.springframework.security.core.context.SecurityContextHolder; 8 | import org.springframework.security.core.userdetails.UserDetails; 9 | import org.springframework.security.core.userdetails.UserDetailsService; 10 | import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; 11 | import org.springframework.stereotype.Component; 12 | import org.springframework.web.bind.annotation.CrossOrigin; 13 | import org.springframework.web.filter.OncePerRequestFilter; 14 | 15 | import io.jsonwebtoken.ExpiredJwtException; 16 | import jakarta.servlet.FilterChain; 17 | import jakarta.servlet.ServletException; 18 | import jakarta.servlet.http.HttpServletRequest; 19 | import jakarta.servlet.http.HttpServletResponse; 20 | import lombok.RequiredArgsConstructor; 21 | import vn.webapp.backend.auction.model.Token; 22 | import vn.webapp.backend.auction.repository.TokenRepository; 23 | import vn.webapp.backend.auction.service.jwt.JwtService; 24 | 25 | @Component 26 | @RequiredArgsConstructor 27 | public class JwtAuthenticationFilter extends OncePerRequestFilter { 28 | 29 | private final JwtService jwtService; 30 | private final UserDetailsService userDetailsService; 31 | private final TokenRepository tokenRepository; 32 | 33 | @Override 34 | @CrossOrigin 35 | protected void doFilterInternal( 36 | HttpServletRequest request, 37 | HttpServletResponse response, 38 | FilterChain filterChain 39 | ) throws ServletException, IOException { 40 | if (request.getServletPath().contains("/api/v1/auth")) { 41 | filterChain.doFilter(request, response); 42 | return; 43 | } 44 | final String authHeader = request.getHeader("Authorization"); 45 | try { 46 | if (authHeader == null || !authHeader.startsWith("Bearer ")) { 47 | filterChain.doFilter(request, response); 48 | return; 49 | } 50 | String jwt = authHeader.substring(7); 51 | String username = jwtService.extractUsername(jwt); 52 | if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { 53 | UserDetails userDetails = this.userDetailsService.loadUserByUsername(username); 54 | Optional tokenOptional = tokenRepository.findByToken(jwt); 55 | if (tokenOptional.isPresent()) { 56 | Token token = tokenOptional.get(); 57 | if (!token.isExpired() && !token.isRevoked() && jwtService.isTokenValid(jwt, userDetails)) { 58 | UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( 59 | userDetails, 60 | null, 61 | userDetails.getAuthorities() 62 | ); 63 | authToken.setDetails( 64 | new WebAuthenticationDetailsSource().buildDetails(request)); 65 | SecurityContextHolder.getContext().setAuthentication(authToken); 66 | } else { 67 | response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Token is expired or revoked"); 68 | return; 69 | } 70 | } else { 71 | response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid token"); 72 | return; 73 | } 74 | } 75 | filterChain.doFilter(request, response); 76 | } catch (ExpiredJwtException ex) { 77 | response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "JWT token is expired!"); 78 | } 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/repository/AuctionRepository.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.repository; 2 | 3 | import org.springframework.data.domain.Page; 4 | import org.springframework.data.domain.Pageable; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | import org.springframework.data.jpa.repository.Query; 7 | import org.springframework.data.repository.query.Param; 8 | import vn.webapp.backend.auction.enums.AuctionState; 9 | import vn.webapp.backend.auction.model.Auction; 10 | 11 | import java.sql.Timestamp; 12 | import java.util.List; 13 | 14 | public interface AuctionRepository extends JpaRepository { 15 | 16 | @Query("SELECT a FROM Auction a WHERE a.startDate >= :startday AND a.startDate <= :endday AND a.state <> 'DELETED'") 17 | Page findAuctionSortByBetweenStartDayAndEndDay(@Param("startday") Timestamp startday, @Param("endday") Timestamp endday, Pageable pageable); 18 | 19 | @Query("SELECT a FROM Auction a WHERE :auctionName = '' OR a.name LIKE %:auctionName%") 20 | List findAuctionByNameContaining(@Param("auctionName") String auctionName); 21 | 22 | @Query("SELECT a FROM Auction a WHERE (:auctionState IS NULL AND a.state <> 'DELETED') OR a.state = :auctionState") 23 | List findByState(@Param("auctionState") AuctionState auctionState); 24 | 25 | @Query("SELECT a FROM Auction a WHERE ((:auctionState IS NULL AND a.state <> 'DELETED') OR (a.state = :auctionState)) AND (:auctionName IS NULL OR a.name LIKE %:auctionName%) ORDER BY a.state DESC") 26 | List findByState(@Param("auctionState") AuctionState auctionState, @Param("auctionName") String auctionName); 27 | 28 | @Query("SELECT a FROM Auction a WHERE " + 29 | "(:auctionState IS NULL OR a.state = :auctionState) " + 30 | "AND (:categoryId = 0 OR a.jewelry.category.id = :categoryId) " + 31 | "AND (:auctionName IS NULL OR a.name LIKE %:auctionName%) ORDER BY a.state DESC") 32 | Page findByStateAndCategoryNotDeletedOrEmptyState( 33 | @Param("auctionState") AuctionState auctionState, 34 | @Param("auctionName") String auctionName, 35 | Pageable pageable, 36 | @Param("categoryId") Integer categoryId 37 | ); 38 | 39 | @Query("SELECT a FROM Auction a WHERE a.user.id = :staff_id AND (:auctionName IS NULL OR a.name LIKE %:auctionName%)") 40 | Page findByStaffID(@Param("staff_id") Integer staff_id, @Param("auctionName") String auctionName, Pageable pageable); 41 | 42 | Page findByStateIn(List states, Pageable pageable); 43 | 44 | @Query("SELECT a FROM Auction a WHERE a.jewelry.id = :jewelryId AND a.state != 'DELETE'") 45 | List findAuctionByJewelryId(@Param("jewelryId") Integer jewelryId); 46 | 47 | @Query("SELECT COUNT(a) FROM Auction a WHERE a.state = 'FINISHED' AND a.lastPrice IS NULL " + 48 | "AND a.id NOT IN (SELECT ah.auction.id FROM AuctionHistory ah) AND YEAR(a.endDate) = :year AND MONTH(a.endDate) = :month") 49 | Integer countAllAuctionsFailed(@Param("month") Integer month, @Param("year") Integer year); 50 | 51 | @Query("SELECT COUNT(a) FROM Auction a WHERE a.state = 'FINISHED' " + 52 | "AND a.id IN (SELECT ah.auction.id FROM AuctionHistory ah) AND YEAR(a.endDate) = :year AND MONTH(a.endDate) = :month") 53 | Integer countAllAuctionsSuccessful(@Param("month") Integer month, @Param("year") Integer year); 54 | 55 | @Query("SELECT COUNT(a) FROM Auction a " + 56 | "WHERE MONTH(a.createDate) = :month AND YEAR(a.createDate) = :year") 57 | Integer countAuctionsByMonthAndYear(@Param("month") Integer month, @Param("year") Integer year); 58 | 59 | @Query("SELECT a FROM Auction a " + 60 | "WHERE a.lastPrice IS NULL " + 61 | "AND a.state = 'FINISHED' " + 62 | "AND (:auctionName IS NULL OR a.name LIKE %:auctionName%)") 63 | Page findAuctionFailedAndName(Pageable pageable, @Param("auctionName") String auctionName); 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/service/email/EmailService.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.service.email; 2 | 3 | import jakarta.mail.MessagingException; 4 | import jakarta.mail.internet.MimeMessage; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.beans.factory.annotation.Value; 7 | import org.springframework.mail.SimpleMailMessage; 8 | import org.springframework.mail.javamail.JavaMailSender; 9 | import org.springframework.mail.javamail.MimeMessageHelper; 10 | import org.springframework.scheduling.annotation.Async; 11 | import org.springframework.stereotype.Service; 12 | import vn.webapp.backend.auction.config.frontend.FrontendConfiguration; 13 | 14 | @Service 15 | @RequiredArgsConstructor 16 | public class EmailService { 17 | private final JavaMailSender javaMailSender; 18 | private final EmailContent emailContent; 19 | 20 | SimpleMailMessage mailMessage = new SimpleMailMessage(); 21 | 22 | @Value("${spring.mail.username}") 23 | private String emailUsername; 24 | 25 | private final FrontendConfiguration frontendConfiguration; 26 | 27 | @Async 28 | public void sendActivationEmail(String emailTo, String fullName, String token) throws MessagingException { 29 | MimeMessage message = javaMailSender.createMimeMessage(); 30 | MimeMessageHelper helper = new MimeMessageHelper(message, true); 31 | 32 | String url = frontendConfiguration.getBaseUrl() + "/activation/" + token; 33 | 34 | String html = emailContent.setHtmlContent(fullName, "Kích hoạt tài khoản", url, 35 | "Cảm ơn bạn đã đăng ký tài khoản tại DGS.", 36 | "Vui lòng nhấn nút bên dưới để kích hoạt tài khoản: "); 37 | 38 | helper.setFrom(emailUsername); 39 | helper.setTo(emailTo); 40 | helper.setSubject("Kích hoạt tài khoản tại DGS."); 41 | helper.setText(html, true); 42 | 43 | javaMailSender.send(message); 44 | } 45 | 46 | @Async 47 | public void sendResetPasswordEmail(String to, String fullName, String token) throws MessagingException { 48 | MimeMessage message = javaMailSender.createMimeMessage(); 49 | MimeMessageHelper helper = new MimeMessageHelper(message, true); 50 | 51 | String url = frontendConfiguration.getBaseUrl() + "/reset-mat-khau/" + token; 52 | 53 | String html = emailContent.setHtmlContent(fullName, "Đặt lại mật khẩu", url, 54 | "Bạn vừa yêu cầu đặt lại mật khẩu tại DGS", 55 | "Vui lòng nhấn nút bên dưới để đặt lại mật khẩu: "); 56 | 57 | helper.setFrom(emailUsername); 58 | helper.setTo(to); 59 | helper.setSubject("Đặt lại mật khẩu tại DGS."); 60 | helper.setText(html, true); 61 | 62 | javaMailSender.send(message); 63 | } 64 | 65 | @Async 66 | public void sendConfirmHoldingEmail(String to, String fullName, String assetName) throws MessagingException { 67 | MimeMessage message = javaMailSender.createMimeMessage(); 68 | MimeMessageHelper helper = new MimeMessageHelper(message, true); 69 | 70 | String html = emailContent.setHtmlConfirmHoldingContent(fullName,assetName); 71 | 72 | helper.setFrom(emailUsername); 73 | helper.setTo(to); 74 | helper.setSubject("Xác nhận tài sản được gửi tới DGS thành công ."); 75 | helper.setText(html, true); 76 | javaMailSender.send(message); 77 | } 78 | 79 | @Async 80 | public void sendBanParticipatingAccountEmail(String to, String fullName, String userName, String reason) throws MessagingException { 81 | MimeMessage message = javaMailSender.createMimeMessage(); 82 | MimeMessageHelper helper = new MimeMessageHelper(message, true); 83 | 84 | String html = emailContent.setHtmlBanParticipatingAccountContent(fullName,userName,reason); 85 | 86 | helper.setFrom(emailUsername); 87 | helper.setTo(to); 88 | helper.setSubject("Tài khoản DGS của bạn sẽ bị tạm khóa, không thể tham gia đấu giá!."); 89 | helper.setText(html, true); 90 | 91 | javaMailSender.send(message); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/service/auction_registration/AuctionRegistrationServiceImpl.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.service.auction_registration; 2 | 3 | import jakarta.transaction.Transactional; 4 | import lombok.RequiredArgsConstructor; 5 | import org.springframework.data.domain.Page; 6 | import org.springframework.data.domain.Pageable; 7 | import org.springframework.stereotype.Service; 8 | import vn.webapp.backend.auction.enums.*; 9 | import vn.webapp.backend.auction.exception.ResourceNotFoundException; 10 | import vn.webapp.backend.auction.exception.UserNotAllowedAccess; 11 | import vn.webapp.backend.auction.model.*; 12 | import vn.webapp.backend.auction.repository.AuctionRegistrationRepository; 13 | import vn.webapp.backend.auction.repository.AuctionRepository; 14 | import vn.webapp.backend.auction.repository.TransactionRepository; 15 | import vn.webapp.backend.auction.repository.UserRepository; 16 | 17 | import java.sql.Timestamp; 18 | import java.time.LocalDateTime; 19 | import java.time.ZoneId; 20 | import java.util.List; 21 | 22 | @Service 23 | @RequiredArgsConstructor 24 | @Transactional 25 | public class AuctionRegistrationServiceImpl implements AuctionRegistrationService { 26 | 27 | private final AuctionRegistrationRepository auctionRegistrationRepository; 28 | private final UserRepository userRepository; 29 | private final AuctionRepository auctionRepository; 30 | private final TransactionRepository transactionRepository; 31 | 32 | @Override 33 | public void registerUserForAuction(String username, Integer auctionId, String transactionCode, String bankCode) { 34 | User user = userRepository.findByUsername(username).orElseThrow(() -> new ResourceNotFoundException(ErrorMessages.USER_NOT_FOUND)); 35 | Auction auction = auctionRepository.findById(auctionId).orElseThrow(() -> new ResourceNotFoundException(ErrorMessages.AUCTION_NOT_FOUND)); 36 | 37 | if (user.getState() != AccountState.VERIFIED) { 38 | throw new UserNotAllowedAccess(ErrorMessages.USER_NOT_VERIFIED); 39 | } 40 | 41 | double registrationFee = auction.getParticipationFee() + auction.getDeposit(); 42 | 43 | Transaction transaction = Transaction.builder() 44 | .createDate(Timestamp.valueOf(LocalDateTime.now(ZoneId.of("Asia/Ho_Chi_Minh")))) 45 | .paymentTime(Timestamp.valueOf(LocalDateTime.now(ZoneId.of("Asia/Ho_Chi_Minh")))) 46 | .totalPrice(registrationFee) 47 | .feesIncurred(0.0) 48 | .state(TransactionState.SUCCEED) 49 | .auction(auction) 50 | .user(user) 51 | .type(TransactionType.REGISTRATION) 52 | .paymentMethod(PaymentMethod.BANKING) 53 | .transactionCode(transactionCode) 54 | .bankCode(bankCode) 55 | .build(); 56 | 57 | // Save the transaction to the database 58 | transactionRepository.save(transaction); 59 | AuctionRegistration registration = AuctionRegistration.builder() 60 | .user(user) 61 | .auction(auction) 62 | .auctionRegistrationState(AuctionRegistrationState.VALID) 63 | .registrationDate(Timestamp.valueOf(LocalDateTime.now(ZoneId.of("UTC")))) 64 | .registrationFee(registrationFee) 65 | .transaction(transaction) 66 | .build(); 67 | 68 | // Save the registration to the database 69 | auctionRegistrationRepository.save(registration); 70 | } 71 | 72 | 73 | @Override 74 | public List findByAuctionIdAndValid(Integer auctionId) { 75 | var auction = auctionRepository.findById(auctionId).orElseThrow(() -> new ResourceNotFoundException(ErrorMessages.AUCTION_NOT_FOUND)); 76 | return auctionRegistrationRepository.findByAuctionIdAndValid(auction.getId()); 77 | } 78 | 79 | @Override 80 | public Page findByUserIdAndValid(Integer userId,String auctionName, Pageable pageable) { 81 | return auctionRegistrationRepository.findByUserIdAndValid(userId,auctionName,pageable); 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/test/java/vn/webapp/backend/auction/AuctionServiceTest.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.ActiveProfiles; 7 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 8 | import vn.webapp.backend.auction.enums.AuctionState; 9 | import vn.webapp.backend.auction.exception.ResourceNotFoundException; 10 | import vn.webapp.backend.auction.model.Auction; 11 | import vn.webapp.backend.auction.service.auction.AuctionService; 12 | 13 | import java.sql.Timestamp; 14 | import java.time.LocalDate; 15 | import java.time.LocalDateTime; 16 | import java.time.LocalTime; 17 | import java.util.Arrays; 18 | import java.util.List; 19 | 20 | import static org.testng.Assert.*; 21 | 22 | @SpringBootTest 23 | @ActiveProfiles("test") 24 | public class AuctionServiceTest extends AbstractTestNGSpringContextTests{ 25 | 26 | @Autowired 27 | private AuctionService auctionService; 28 | 29 | @Test 30 | public void testGetAllReturnsWell(){ 31 | // Act 32 | List auctions = auctionService.getAll(); 33 | 34 | // Assert 35 | assertNotNull(auctions); 36 | assertFalse(auctions.isEmpty()); 37 | } 38 | 39 | @Test 40 | public void testGetAuctionByIdReturnsWell(){ 41 | // Expected 42 | Integer id = 1; 43 | 44 | // Act 45 | Auction auction = auctionService.getAuctionById(id); 46 | 47 | // Assert 48 | assertNotNull(auction); 49 | assertEquals(auction.getId(), id); 50 | } 51 | 52 | @Test 53 | public void testFindAuctionByNameReturnsWell(){ 54 | // Expected 55 | String name = "nhẫn"; 56 | // Act 57 | List auctions = auctionService.findAuctionByName(name); 58 | // Assert 59 | assertNotNull(auctions); 60 | assertFalse(auctions.isEmpty()); 61 | // Verify that each returned Jewelry item belongs to the expected username 62 | for (Auction auction : auctions) { 63 | assertTrue(auction.getName().toLowerCase().contains(name.toLowerCase())); 64 | } 65 | } 66 | 67 | @Test 68 | public void testGetAuctionByStateReturnsWell() { 69 | // Expected 70 | AuctionState state = AuctionState.ONGOING; 71 | // Act 72 | List auctions = auctionService.getAuctionByState(state); 73 | // Assert 74 | assertNotNull(auctions); 75 | assertFalse(auctions.isEmpty()); 76 | for (Auction auction : auctions) { 77 | assertEquals(state, auction.getState()); 78 | } 79 | } 80 | 81 | @Test 82 | public void testGetCurrentAuctionByJewelryIdReturnsWell() { 83 | // Expected 84 | Integer jewelryId = 1; 85 | // Act 86 | Auction auction = auctionService.getCurrentAuctionByJewelryId(jewelryId); 87 | // Assert 88 | assertNotNull(auction); 89 | assertEquals(auction.getJewelry().getId(), jewelryId); 90 | } 91 | // =================================================================== 92 | @Test 93 | public void testGetAuctionByIdReturnsNull() { 94 | // Expected 95 | Integer nonExistId = 99; 96 | 97 | // Act and Assert 98 | assertThrows(ResourceNotFoundException.class, () -> auctionService.getAuctionById(nonExistId)); 99 | } 100 | 101 | @Test 102 | public void testFindAuctionByNameReturnsNullWhenNoAuctionsFound() { 103 | // Expected 104 | String nonExistentName = "nonexistentname"; 105 | 106 | // Act 107 | List auctions = auctionService.findAuctionByName(nonExistentName); 108 | 109 | // Assert 110 | assertNotNull(auctions); 111 | assertTrue(auctions.isEmpty()); 112 | } 113 | 114 | @Test 115 | public void testGetAuctionByStateReturnsEmptyList() { 116 | // Expected 117 | AuctionState state = AuctionState.DELETED; 118 | 119 | // Act 120 | List auctions = auctionService.getAuctionByState(state); 121 | 122 | // Assert 123 | assertNotNull(auctions); 124 | assertTrue(auctions.isEmpty()); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/controller/AuctionHistoryController.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.controller; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.data.domain.Page; 5 | import org.springframework.data.domain.PageRequest; 6 | import org.springframework.data.domain.Pageable; 7 | import org.springframework.data.domain.Sort; 8 | import org.springframework.http.ResponseEntity; 9 | import org.springframework.web.bind.annotation.*; 10 | import vn.webapp.backend.auction.dto.BidRequest; 11 | import vn.webapp.backend.auction.enums.AuctionHistoryState; 12 | import vn.webapp.backend.auction.model.AuctionHistory; 13 | import vn.webapp.backend.auction.service.bid.AuctionHistoryService; 14 | 15 | import java.util.List; 16 | 17 | @RestController 18 | @CrossOrigin(origins = "http://localhost:3000") 19 | @RequiredArgsConstructor 20 | @RequestMapping("/api/v1/auction-history") 21 | public class AuctionHistoryController { 22 | 23 | private final AuctionHistoryService auctionHistoryService; 24 | 25 | @GetMapping("/get-by-auction") 26 | public ResponseEntity> getAuctionHistoryByAuctionId( 27 | @RequestParam(defaultValue = "time") String sortBy, 28 | @RequestParam(defaultValue = "0") Integer id, 29 | @RequestParam(defaultValue = "0") int page, 30 | @RequestParam(defaultValue = "3") int size, 31 | @RequestParam(defaultValue = "desc") String sortOrder) { 32 | Sort.Direction direction = (sortOrder.equalsIgnoreCase("desc")) ? Sort.Direction.DESC : Sort.Direction.ASC; 33 | Pageable pageable = PageRequest.of(page, size, direction, sortBy); 34 | return ResponseEntity.ok(auctionHistoryService.getAuctionHistoryByAuctionId(pageable, id)); 35 | } 36 | 37 | @GetMapping("/get-by-auction-and-user") 38 | public ResponseEntity> getAuctionHistoryByAuctionIdAndUserId( 39 | @RequestParam(defaultValue = "time") String sortBy, 40 | @RequestParam(defaultValue = "0") Integer auctionId, 41 | @RequestParam(defaultValue = "0") Integer userId, 42 | @RequestParam(defaultValue = "0") int page, 43 | @RequestParam(defaultValue = "5") int size, 44 | @RequestParam(defaultValue = "ACTIVE") AuctionHistoryState state, 45 | @RequestParam(defaultValue = "desc") String sortOrder) { 46 | Sort.Direction direction = (sortOrder.equalsIgnoreCase("desc")) ? Sort.Direction.DESC : Sort.Direction.ASC; 47 | Pageable pageable = PageRequest.of(page, size, direction, sortBy); 48 | return ResponseEntity.ok(auctionHistoryService.getAuctionHistoryByAuctionIdAndUserId(auctionId, userId, state, pageable)); 49 | } 50 | 51 | @GetMapping("/get-by-username") 52 | public ResponseEntity> getAuctionHistoryByUsername( 53 | @RequestParam(defaultValue = "time") String sortBy, 54 | @RequestParam(defaultValue = "0") String username, 55 | @RequestParam(defaultValue = "0") int page, 56 | @RequestParam(defaultValue = "10") int size, 57 | @RequestParam(defaultValue = "desc") String sortOrder) { 58 | Sort.Direction direction = (sortOrder.equalsIgnoreCase("desc")) ? Sort.Direction.DESC : Sort.Direction.ASC; 59 | Pageable pageable = PageRequest.of(page, size, direction, sortBy); 60 | return ResponseEntity.ok(auctionHistoryService.getAuctionHistoryByUsername(pageable, username)); 61 | } 62 | 63 | @GetMapping("/get-by-date/{date}") 64 | public ResponseEntity> getAuctionHistoryByDate(@PathVariable String date) { //2024-12-10 65 | return ResponseEntity.ok(auctionHistoryService.getAuctionHistoryByDate(date)); 66 | } 67 | 68 | 69 | @GetMapping("/get-when-auction-finished/{id}") 70 | public ResponseEntity> getAuctionHistoryByAuctionIdWhenFinished(@PathVariable Integer id) { //2024-12-10 71 | return ResponseEntity.ok(auctionHistoryService.getAuctionHistoryByAuctionIdWhenFinished(id)); 72 | } 73 | 74 | @PostMapping 75 | public ResponseEntity saveBidByUserAndAuction(@RequestBody BidRequest request) { 76 | auctionHistoryService.saveBidByUserAndAuction(request); 77 | return ResponseEntity.ok().build(); 78 | } 79 | 80 | @DeleteMapping("/bids/{userId}/{auctionId}") 81 | public ResponseEntity deleteBidByUserAndAuction(@PathVariable Integer userId, @PathVariable Integer auctionId, @RequestParam String reason) { 82 | auctionHistoryService.deleteBidByUserAndAuction(userId, auctionId, reason); 83 | return ResponseEntity.ok().build(); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/service/jwt/JwtService.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.service.jwt; 2 | 3 | import io.jsonwebtoken.Claims; 4 | import io.jsonwebtoken.Jwts; 5 | import io.jsonwebtoken.SignatureAlgorithm; 6 | import io.jsonwebtoken.io.Decoders; 7 | import io.jsonwebtoken.security.Keys; 8 | import jakarta.servlet.http.Cookie; 9 | import jakarta.servlet.http.HttpServletRequest; 10 | import lombok.RequiredArgsConstructor; 11 | 12 | import java.security.Key; 13 | import java.util.Date; 14 | import java.util.HashMap; 15 | import java.util.Map; 16 | import java.util.function.Function; 17 | 18 | import org.springframework.beans.factory.annotation.Value; 19 | import org.springframework.http.ResponseCookie; 20 | import org.springframework.security.core.userdetails.UserDetails; 21 | import org.springframework.stereotype.Service; 22 | 23 | @Service 24 | @RequiredArgsConstructor 25 | public class JwtService { 26 | 27 | @Value("${application.security.jwt.secret-key}") 28 | public String secretKey; 29 | 30 | @Value("${application.security.jwt.expiration}") 31 | public long jwtExpiration; 32 | 33 | @Value("${application.security.jwt.refresh-token.expiration}") 34 | public long refreshExpiration; 35 | 36 | public static final String REFRESH_SECRET_KEY = "sUpErSeCrEtReFrEsHkEy1234567890abcdefghijklmnopqrstuv"; 37 | 38 | public String generateToken(UserDetails userDetails) { 39 | return generateToken(new HashMap<>(), userDetails); 40 | } 41 | 42 | public String generateToken( 43 | Map extraClaims, 44 | UserDetails userDetails 45 | ) { 46 | return buildToken(extraClaims, userDetails, jwtExpiration); 47 | } 48 | 49 | public String generateRefreshToken( 50 | UserDetails userDetails 51 | ) { 52 | return buildToken(new HashMap<>(), userDetails, refreshExpiration); 53 | } 54 | 55 | public ResponseCookie generateRefreshTokenCookie(String token) { 56 | return ResponseCookie.from("refresh_token", token) 57 | .path("/") 58 | .maxAge(refreshExpiration) // 15 days in seconds 59 | .httpOnly(true) 60 | .secure(true) 61 | .sameSite("Strict") 62 | .build(); 63 | } 64 | 65 | private String buildToken(Map extraClaims, 66 | UserDetails userDetails, 67 | long expiration) { 68 | return Jwts 69 | .builder() 70 | .setClaims(extraClaims) 71 | .claim("authorities", userDetails.getAuthorities()) 72 | .setSubject(userDetails.getUsername()) 73 | .setIssuedAt(new Date(System.currentTimeMillis())) 74 | .setExpiration(new Date(System.currentTimeMillis() + expiration 75 | )) 76 | .signWith(getSignInKey(), SignatureAlgorithm.HS256) 77 | .compact(); 78 | } 79 | 80 | private Key getSignInKey() { 81 | byte[] keyBytes = Decoders.BASE64.decode(secretKey); 82 | return Keys.hmacShaKeyFor(keyBytes); 83 | } 84 | 85 | private Claims extractAllClaims(String token) { 86 | return Jwts 87 | .parserBuilder() 88 | .setSigningKey(getSignInKey()) 89 | .build() 90 | .parseClaimsJws(token) 91 | .getBody(); 92 | } 93 | 94 | public T extractClaim(String token, Function claimsResolver) { 95 | final Claims claims = extractAllClaims(token); 96 | return claimsResolver.apply(claims); 97 | } 98 | 99 | public String extractUsername(String token) { 100 | return extractClaim(token, Claims::getSubject); 101 | } 102 | 103 | public boolean isTokenValid(String token, UserDetails userDetails) { 104 | final String username = extractUsername(token); 105 | return (username.equals(userDetails.getUsername())) && !isTokenExpired(token); 106 | } 107 | 108 | public boolean isTokenExpired(String token) { 109 | return extractExpiration(token).before(new Date()); 110 | } 111 | 112 | private Date extractExpiration(String token) { 113 | return extractClaim(token, Claims::getExpiration); 114 | } 115 | 116 | public boolean isRefreshTokenValid(String token, UserDetails userDetails) { 117 | return isTokenValid(token, userDetails); 118 | } 119 | 120 | 121 | public String getTokenFromCookie(HttpServletRequest request, String cookieName) { 122 | Cookie[] cookies = request.getCookies(); 123 | if (cookies != null) { 124 | for (Cookie cookie : cookies) { 125 | if (cookie.getName().equals(cookieName)) { 126 | return cookie.getValue(); 127 | } 128 | } 129 | } 130 | return null; // Cookie not found 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/repository/UserRepository.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.repository; 2 | 3 | import org.springframework.data.domain.Page; 4 | import org.springframework.data.domain.Pageable; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | import org.springframework.data.jpa.repository.Query; 7 | import org.springframework.data.repository.query.Param; 8 | import vn.webapp.backend.auction.enums.AccountState; 9 | import vn.webapp.backend.auction.enums.Role; 10 | import vn.webapp.backend.auction.model.AuctionRegistration; 11 | import vn.webapp.backend.auction.model.User; 12 | 13 | import java.util.List; 14 | import java.util.Optional; 15 | 16 | public interface UserRepository extends JpaRepository { 17 | 18 | Optional findByEmail(String email); 19 | 20 | Optional findByUsername(String usename); 21 | 22 | @Query("SELECT u FROM User u WHERE u.role = :role") 23 | List findAllByRole(@Param("role") Role role); 24 | 25 | @Query("UPDATE User u SET u.avatar = :avatar WHERE u.username = :username") 26 | void updateAvatar(String username, String avatar); 27 | 28 | @Query("SELECT u FROM User u " + "WHERE (:fullName IS NULL OR CONCAT(u.firstName,' ',u.lastName) " + 29 | "LIKE %:fullName%) " + "AND (:role IS NULL OR u.role = :role) " + 30 | "AND (:state IS NULL OR u.state = :state)") 31 | Page findByFullNameContainingAndRoleAndState(@Param("fullName") String fullName, @Param("role") Role role, @Param("state") AccountState state, Pageable pageable); 32 | 33 | @Query("SELECT u FROM User u " + "WHERE (:fullName IS NULL OR CONCAT(u.firstName,' ',u.lastName) LIKE %:fullName%) " + "AND (:role IS NULL OR u.role = :role) " + "AND (:state IS NULL OR u.state <> :state)") 34 | Page findByFullNameContainingAndRoleAndNotState(@Param("fullName") String fullName, @Param("role") Role role, @Param("state") AccountState state, Pageable pageable); 35 | 36 | @Query("SELECT u FROM User u " + 37 | "WHERE (:fullName IS NULL OR CONCAT(u.firstName, ' ', u.lastName) LIKE %:fullName%) " + 38 | "AND (:state IS NULL OR u.state <> :state AND u.state <> 'DISABLE') AND u.cccdFirst IS NOT NULL AND u.cccdLast IS NOT NULL") 39 | Page findByFullNameContainingAndStateNot(@Param("fullName") String fullName, @Param("state") AccountState state, Pageable pageable); 40 | 41 | @Query("SELECT ah.user " + 42 | "FROM AuctionHistory ah " + 43 | "WHERE ah.auction.id = :auctionId " + 44 | "AND ah.time = (SELECT MAX(ah2.time) FROM AuctionHistory ah2 WHERE ah2.auction.id = :auctionId AND ah2.state='ACTIVE')") 45 | Optional findLatestUserInAuctionHistoryByAuctionId(@Param("auctionId") Integer auctionId); 46 | 47 | @Query("SELECT a.jewelry.user " + 48 | "FROM Auction a " + 49 | "WHERE a.id = :auctionId ") 50 | Optional findOwnerByAuctionId(@Param("auctionId") Integer auctionId); 51 | 52 | @Query("SELECT u FROM User u JOIN AuctionRegistration ar ON u.id = ar.user.id WHERE ar.auction.id = :auctionId AND u.id <> :winnerId AND ar.auctionRegistrationState = 'VALID'") 53 | List findUsersInAuctionHistoryByAuctionIdExceptWinner(@Param("auctionId") Integer auctionId, @Param("winnerId") Integer winnerId); 54 | 55 | @Query("SELECT COUNT(u) FROM User u WHERE u.state != 'DISABLE'") 56 | Integer getTotalUser(); 57 | 58 | @Query("SELECT COUNT(u) FROM User u WHERE u.state = :state") 59 | Integer getTotalUserByState(@Param("state") AccountState state); 60 | 61 | @Query("SELECT COUNT(u) FROM User u WHERE u.role = :role") 62 | Integer getTotalUserByRole(@Param("role") Role role); 63 | 64 | @Query("SELECT COUNT(u) FROM User u WHERE MONTH(u.registerDate) = :month AND YEAR(u.registerDate) = :year") 65 | Integer getTotalUserByMonthAndYear(@Param("month") Integer month, @Param("year") Integer year); 66 | 67 | @Query("SELECT ar.user FROM AuctionRegistration ar WHERE ar.auction.id = :auctionId AND ar.auctionRegistrationState = 'VALID'") 68 | List findByAuctionIdAndUserIdValid(@Param("auctionId") Integer auctionId); 69 | 70 | @Query(nativeQuery = true, 71 | value = "SELECT TOP 5 u.* " + 72 | "FROM [Transaction] t " + 73 | "JOIN [User] u ON t.user_id = u.id " + 74 | "WHERE t.transaction_type = 'PAYMENT_TO_WINNER' " + 75 | " AND t.transaction_state = 'SUCCEED' " + 76 | "GROUP BY u.id, u.cccd, u.address, u.avatar, u.bank_account_name, " + 77 | " u.bank_account_number, u.bank_id, u.city, u.district, " + 78 | " u.email, u.first_name, u.last_name, u.phone, u.password, " + 79 | " u.role, u.state, u.register_date, u.username, u.ward, u.year_of_birth, " + 80 | " u.cccd_first, u.cccd_last, u.cccd_from, u.ban_reason " + 81 | "ORDER BY SUM(t.total_price) DESC") 82 | List findTopUsersByTotalSpent(); 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/config/security/SecurityConfiguration.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.config.security; 2 | 3 | import jakarta.servlet.http.HttpServletResponse; 4 | import lombok.RequiredArgsConstructor; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.http.HttpMethod; 8 | import org.springframework.security.authentication.AuthenticationProvider; 9 | import org.springframework.security.config.Customizer; 10 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 11 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 12 | import org.springframework.security.config.http.SessionCreationPolicy; 13 | import org.springframework.security.core.context.SecurityContextHolder; 14 | import org.springframework.security.web.SecurityFilterChain; 15 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; 16 | import org.springframework.security.web.authentication.logout.LogoutHandler; 17 | import org.springframework.web.bind.annotation.CrossOrigin; 18 | import vn.webapp.backend.auction.enums.Role; 19 | import vn.webapp.backend.auction.filter.JwtAuthenticationFilter; 20 | import vn.webapp.backend.auction.security.Endpoints; 21 | 22 | @Configuration 23 | @EnableWebSecurity 24 | @RequiredArgsConstructor 25 | public class SecurityConfiguration { 26 | private static final String[] WHITE_LIST_URL = { 27 | "/api/v1/auth/**", 28 | "/v2/api-docs", 29 | "/v3/api-docs", 30 | "/v3/api-docs/**", 31 | "/swagger-resources", 32 | "/swagger-resources/**", 33 | "/configuration/ui", 34 | "/configuration/security", 35 | "/swagger-ui/**", 36 | "/webjars/**", 37 | "/swagger-ui.html"}; 38 | private final AuthenticationProvider authenticationProvider; 39 | private final JwtAuthenticationFilter jwtAuthenticationFilter; 40 | private final LogoutHandler logoutHandler; 41 | 42 | @Bean 43 | @CrossOrigin 44 | public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { 45 | 46 | http 47 | .authorizeHttpRequests( 48 | configurer -> configurer 49 | .requestMatchers(WHITE_LIST_URL).permitAll() 50 | .requestMatchers("/ws/**").permitAll() 51 | .requestMatchers("/api/v1/auth/**").permitAll() 52 | .requestMatchers(HttpMethod.GET, Endpoints.PUBLIC_GET_ENDPOINTS).permitAll() 53 | .requestMatchers(HttpMethod.POST, Endpoints.PUBLIC_POST_ENDPOINTS).permitAll() 54 | .requestMatchers(HttpMethod.PUT, Endpoints.PUBLIC_PUT_ENDPOINTS).permitAll() 55 | 56 | .requestMatchers(HttpMethod.GET, Endpoints.STAFF_GET_ENDPOINTS).hasAuthority(Role.STAFF.name()) 57 | 58 | .requestMatchers(HttpMethod.GET, Endpoints.MANAGER_GET_ENDPOINTS).hasAuthority(Role.MANAGER.name()) 59 | .requestMatchers(HttpMethod.POST, Endpoints.MANAGER_POST_ENDPOINTS).hasAuthority(Role.MANAGER.name()) 60 | .requestMatchers(HttpMethod.PUT, Endpoints.MANAGER_PUT_ENDPOINTS).hasAuthority(Role.MANAGER.name()) 61 | .requestMatchers(HttpMethod.DELETE, Endpoints.MANAGER_DELETE_ENDPOINTS).hasAuthority(Role.MANAGER.name()) 62 | 63 | .requestMatchers(HttpMethod.GET, Endpoints.MANAGER_ADMIN_GET_ENDPOINTS).hasAnyAuthority(Role.MANAGER.name(),Role.ADMIN.name()) 64 | 65 | .requestMatchers(HttpMethod.GET, Endpoints.ADMIN_GET_ENDPOINTS).hasAuthority(Role.ADMIN.name()) 66 | .requestMatchers(HttpMethod.POST, Endpoints.ADMIN_POST_ENDPOINTS).hasAuthority(Role.ADMIN.name()) 67 | .requestMatchers(HttpMethod.PUT, Endpoints.ADMIN_PUT_ENDPOINTS).hasAuthority(Role.ADMIN.name()) 68 | 69 | .anyRequest().authenticated() 70 | ) 71 | .csrf(csrf -> csrf.disable()) 72 | .sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) 73 | .authenticationProvider(authenticationProvider) 74 | .httpBasic(Customizer.withDefaults()) 75 | .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) 76 | .logout(logout -> 77 | logout.logoutUrl("/api/v1/auth/logout") 78 | .addLogoutHandler(logoutHandler) 79 | .logoutSuccessHandler((request, response, authentication) -> SecurityContextHolder.clearContext()) 80 | ).exceptionHandling(exceptionHandling -> exceptionHandling 81 | .authenticationEntryPoint((request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED)) 82 | ); 83 | return http.build(); 84 | } 85 | } -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/model/User.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import jakarta.persistence.*; 5 | import jakarta.validation.constraints.NotBlank; 6 | import lombok.*; 7 | import org.springframework.security.core.GrantedAuthority; 8 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 9 | import org.springframework.security.core.userdetails.UserDetails; 10 | import vn.webapp.backend.auction.enums.AccountState; 11 | import vn.webapp.backend.auction.enums.Role; 12 | 13 | import java.sql.Timestamp; 14 | import java.util.Collection; 15 | import java.util.List; 16 | 17 | @Entity 18 | @Data 19 | @AllArgsConstructor 20 | @NoArgsConstructor 21 | @Builder 22 | @Table(name = "[user]") 23 | public class User implements UserDetails { 24 | 25 | @Id 26 | @GeneratedValue(strategy = GenerationType.IDENTITY) 27 | private int id; 28 | 29 | @Column(name = "username", unique = true, nullable = false, length = 50) 30 | private String username; 31 | 32 | @JsonIgnore 33 | @NotBlank 34 | @Column(name = "password") 35 | private String password; 36 | 37 | @Column(name = "first_name", nullable = false) 38 | @NotBlank(message = "The first name is required") 39 | private String firstName; 40 | 41 | @Column(name = "last_name", nullable = false) 42 | @NotBlank(message = "The last name is required") 43 | private String lastName; 44 | 45 | @Column(name = "email", nullable = false, length = 50, unique = true) 46 | @NotBlank(message = "The email is required") 47 | private String email; 48 | 49 | @Column(name = "phone", nullable = false, length = 15) 50 | @NotBlank(message = "The phone is required") 51 | private String phone; 52 | 53 | @Column(name = "address", nullable = false) 54 | @NotBlank(message = "The address is required") 55 | private String address; 56 | 57 | @Column(name = "city", nullable = false) 58 | @NotBlank(message = "The city is required") 59 | private String city; 60 | 61 | @Column(name = "district", nullable = false) 62 | @NotBlank(message = "The district is required") 63 | private String district; 64 | 65 | @Column(name = "ward", nullable = false) 66 | @NotBlank(message = "The ward is required") 67 | private String ward; 68 | 69 | @Column(name = "year_of_birth", nullable = false, length = 4) 70 | @NotBlank(message = "The year of birth is required") 71 | private String yob; 72 | 73 | @Column(name = "avatar") 74 | @Lob 75 | private String avatar; 76 | 77 | @Column(name = "CCCD", nullable = false, length = 20) 78 | @NotBlank(message = "The CCCD is required") 79 | private String cccd; 80 | 81 | @Column(name = "CCCD_first") 82 | @Lob 83 | private String cccdFirst; 84 | 85 | @Column(name = "CCCD_last") 86 | @Lob 87 | private String cccdLast; 88 | 89 | @Column(name = "CCCD_from", length = 50) 90 | private String cccdFrom; 91 | 92 | @Column(name = "ban_reason") 93 | private String banReason; 94 | 95 | @Column(name = "register_date") 96 | private Timestamp registerDate; 97 | 98 | @Enumerated(EnumType.STRING) 99 | @Column(name ="state" , nullable = false, length = 20) 100 | private AccountState state; 101 | 102 | @ManyToOne 103 | @JoinColumn(name = "bank_id") 104 | @EqualsAndHashCode.Exclude 105 | @ToString.Exclude 106 | private Bank bank; 107 | 108 | @Column(name ="bank_account_number", nullable = false, length = 30) 109 | private String bankAccountNumber; 110 | 111 | @Column(name ="bank_account_name", nullable = false, length = 30) 112 | private String bankAccountName; 113 | 114 | 115 | @Enumerated(EnumType.STRING) 116 | private Role role; 117 | 118 | @OneToMany(mappedBy = "user", cascade = CascadeType.ALL) 119 | @EqualsAndHashCode.Exclude 120 | @ToString.Exclude 121 | @JsonIgnore 122 | private List tokens; 123 | 124 | @OneToMany(mappedBy = "user", cascade = CascadeType.ALL) 125 | @EqualsAndHashCode.Exclude 126 | @ToString.Exclude 127 | @JsonIgnore 128 | private List jewelries; 129 | 130 | public String getFullName() { 131 | return firstName + " " + lastName; 132 | } 133 | 134 | @JsonIgnore 135 | @Override 136 | public Collection getAuthorities() { 137 | return List.of(new SimpleGrantedAuthority(role.name())); 138 | } 139 | 140 | @Override 141 | @JsonIgnore 142 | public boolean isAccountNonExpired() { 143 | return true; 144 | } 145 | 146 | @Override 147 | @JsonIgnore 148 | public boolean isAccountNonLocked() { 149 | return true; 150 | } 151 | 152 | @Override 153 | @JsonIgnore 154 | public boolean isCredentialsNonExpired() { 155 | return true; 156 | } 157 | 158 | @Override 159 | @JsonIgnore 160 | public boolean isEnabled() { 161 | return true; 162 | } 163 | 164 | @Override 165 | @JsonIgnore 166 | public String getPassword() { 167 | return password; 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 3.2.5 9 | 10 | 11 | vn.webapp.backend 12 | auction 13 | 1.0.0 14 | jar 15 | auction 16 | Project for Spring Boot for Auction Jewelry 17 | 18 | 17 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-data-jpa 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-actuator 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter-security 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-web 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-devtools 40 | runtime 41 | true 42 | 43 | 44 | com.microsoft.sqlserver 45 | mssql-jdbc 46 | runtime 47 | 48 | 49 | com.fasterxml.jackson.core 50 | jackson-databind 51 | 52 | 53 | 54 | mysql 55 | mysql-connector-java 56 | 8.0.33 57 | 58 | 59 | org.projectlombok 60 | lombok 61 | true 62 | 63 | 64 | org.springframework.boot 65 | spring-boot-starter-test 66 | test 67 | 68 | 69 | org.springframework.boot 70 | spring-boot-starter-validation 71 | 72 | 73 | 74 | io.jsonwebtoken 75 | jjwt-api 76 | 0.11.5 77 | 78 | 79 | io.jsonwebtoken 80 | jjwt-impl 81 | 0.11.5 82 | 83 | 84 | io.jsonwebtoken 85 | jjwt-jackson 86 | 0.11.5 87 | 88 | 89 | 90 | 91 | org.springdoc 92 | springdoc-openapi-starter-webmvc-ui 93 | 2.1.0 94 | 95 | 96 | 97 | org.springframework.boot 98 | spring-boot-starter-mail 99 | 100 | 101 | 102 | 103 | org.webjars 104 | sockjs-client 105 | 1.0.2 106 | 107 | 108 | org.webjars 109 | stomp-websocket 110 | 2.3.3 111 | 112 | 113 | org.springframework.boot 114 | spring-boot-starter-websocket 115 | 116 | 117 | 118 | 119 | org.testng 120 | testng 121 | 7.10.2 122 | test 123 | 124 | 125 | 126 | 127 | 128 | 129 | org.springframework.boot 130 | spring-boot-maven-plugin 131 | 132 | 133 | 134 | org.projectlombok 135 | lombok 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /src/main/java/vn/webapp/backend/auction/repository/JewelryRepository.java: -------------------------------------------------------------------------------- 1 | package vn.webapp.backend.auction.repository; 2 | 3 | import org.springframework.data.domain.Page; 4 | import org.springframework.data.domain.Pageable; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | import org.springframework.data.jpa.repository.Query; 7 | import org.springframework.data.repository.query.Param; 8 | import vn.webapp.backend.auction.enums.JewelryState; 9 | import vn.webapp.backend.auction.model.Jewelry; 10 | import vn.webapp.backend.auction.model.RequestApproval; 11 | 12 | import java.util.List; 13 | import java.util.Optional; 14 | 15 | public interface JewelryRepository extends JpaRepository { 16 | 17 | @Query("SELECT j FROM Jewelry j WHERE j.user.username = :username") 18 | List findJewelryByUsername(@Param("username") String username); 19 | 20 | List findJewelryByCategoryId(Integer categoryId); 21 | 22 | List getJewelriesByNameContaining(String name); 23 | 24 | Page findByState(JewelryState jewelryState, Pageable pageable); 25 | 26 | @Query("SELECT j FROM Jewelry j INNER JOIN RequestApproval r ON j.id = r.jewelry.id " + 27 | "WHERE r.sender.role = 'MEMBER' AND r.isConfirm = false AND r.state = 'ACTIVE'") 28 | Page findJewelryInWaitlist(Pageable pageable); 29 | 30 | @Query("SELECT j FROM Jewelry j INNER JOIN Auction a ON j.id = a.jewelry.id WHERE a.state = 'FINISHED'") 31 | Page findJewelryInHandOver(Pageable pageable); 32 | 33 | @Query("SELECT j FROM Jewelry j WHERE j.state = :state AND j.isHolding = :isHolding "+ 34 | "AND (:jewelryName IS NULL OR j.name LIKE %:jewelryName%) AND (:category IS NULL OR j.category.name = :category)") 35 | Page findJewelryByStateAndIsHolding( 36 | @Param("state") JewelryState state, 37 | @Param("isHolding") Boolean isHolding, 38 | @Param("category") String category, 39 | @Param("jewelryName") String jewelryName, 40 | Pageable pageable); 41 | 42 | @Query("SELECT j FROM Jewelry j WHERE j.state = :state "+ 43 | "AND (:jewelryName IS NULL OR j.name LIKE %:jewelryName%) "+ 44 | " AND (:category IS NULL OR j.category.name = :category)") 45 | Page findJewelriesManager( 46 | @Param("state") JewelryState state, 47 | @Param("jewelryName") String jewelryName, 48 | @Param("category") String category, 49 | Pageable pageable); 50 | 51 | @Query("SELECT j FROM Jewelry j WHERE j.state = 'ACTIVE' AND j.isHolding = true " + 52 | "AND j.user.state ='DISABLE'" + 53 | "AND (:jewelryName IS NULL OR j.name LIKE %:jewelryName%) " + 54 | "AND (:category IS NULL OR j.category.name = :category)") 55 | Page findJewelryReturnedViolator(@Param("category") String category, @Param("jewelryName") String jewelryName, Pageable pageable); 56 | 57 | Page findByUserUsername(String username, Pageable pageable); 58 | 59 | @Query("SELECT j FROM Jewelry j ORDER BY j.id DESC") 60 | List findLatestJewelry(); 61 | 62 | @Query("SELECT COUNT(j) FROM Jewelry j WHERE j.isHolding IS NULL AND " + 63 | "j.state = 'APPROVING' AND j.receivedDate IS NULL AND j.deliveryDate IS NULL " + 64 | "AND MONTH(j.createDate) = :month AND YEAR(j.createDate) = :year") 65 | Integer countAllJewelriesNeedPricing(@Param("month") Integer month, @Param("year") Integer year); 66 | 67 | @Query("SELECT COUNT(j) FROM Jewelry j WHERE j.isHolding = false AND " + 68 | "j.state = 'ACTIVE' AND j.receivedDate IS NULL AND j.deliveryDate IS NULL " + 69 | "AND MONTH(j.createDate) = :month AND YEAR(j.createDate) = :year") 70 | Integer countAllJewelriesPriced(@Param("month") Integer month, @Param("year") Integer year); 71 | 72 | @Query("SELECT COUNT(j) FROM Jewelry j WHERE j.isHolding = true AND " + 73 | "j.state = 'ACTIVE' AND j.receivedDate IS NOT NULL AND j.deliveryDate IS NULL " + 74 | "AND MONTH(j.createDate) = :month AND YEAR(j.createDate) = :year") 75 | Integer countAllJewelriesNotHasAuction(@Param("month") Integer month, @Param("year") Integer year); 76 | 77 | @Query("SELECT COUNT(j) FROM Jewelry j WHERE j.isHolding = true AND " + 78 | "j.state = 'AUCTION' AND j.receivedDate IS NOT NULL AND j.deliveryDate IS NULL " + 79 | "AND MONTH(j.createDate) = :month AND YEAR(j.createDate) = :year") 80 | Integer countAllJewelriesHasAuction(@Param("month") Integer month, @Param("year") Integer year); 81 | 82 | @Query("SELECT COUNT(j) FROM Jewelry j WHERE j.isHolding = false AND " + 83 | "j.state = 'HANDED_OVER' AND j.receivedDate IS NOT NULL AND j.deliveryDate IS NOT NULL " + 84 | "AND MONTH(j.createDate) = :month AND YEAR(j.createDate) = :year") 85 | Integer countAllJewelriesHandOver(@Param("month") Integer month, @Param("year") Integer year); 86 | 87 | @Query("SELECT j FROM Jewelry j " + 88 | "WHERE " + 89 | "(:jewelryName IS NULL OR j.name LIKE %:jewelryName%) " + 90 | "AND (:category IS NULL OR j.category.name = :category) " + 91 | "AND j.state = 'ACTIVE' AND j.isHolding = true AND j.user.state != 'DISABLE'") 92 | Page getPassedJewelry( 93 | @Param("jewelryName") String jewelryName, 94 | @Param("category") String category, 95 | Pageable pageable 96 | ); 97 | 98 | @Query("SELECT j FROM Jewelry j WHERE j.user.id = :userId AND (:jewelryName IS NULL OR j.name LIKE %:jewelryName%) AND (j.state = 'ACTIVE' OR j.state = 'AUCTION')") 99 | Page findJewelryActiveByUserId(@Param("userId") Integer userId,@Param("jewelryName") String jewelryName, Pageable pageable); 100 | 101 | } 102 | --------------------------------------------------------------------------------