From bcd53167106f1988696bbf5b4be8a85fc739968d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Tue, 3 Jun 2025 23:01:13 +0900 Subject: [PATCH 01/20] =?UTF-8?q?feat:=20=EC=8A=A4=EC=9B=A8=EA=B1=B0=20JWT?= =?UTF-8?q?=20=ED=86=A0=ED=81=B0=20=EC=9E=90=EB=8F=99=20Authorize=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../homeaid/controller/UserBoardController.java | 2 +- .../java/com/homeaid/config/SwaggerConfig.java | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/board/src/main/java/com/homeaid/controller/UserBoardController.java b/board/src/main/java/com/homeaid/controller/UserBoardController.java index cb2af47e..51dc86c9 100644 --- a/board/src/main/java/com/homeaid/controller/UserBoardController.java +++ b/board/src/main/java/com/homeaid/controller/UserBoardController.java @@ -39,7 +39,7 @@ @RestController @RequiredArgsConstructor -@RequestMapping("/api/v1/board") +@RequestMapping("/api/v1/boards") @Tag(name = "1:1 문의글", description = "사용자와 관리자 간 1:1 문의글 API") @SecurityRequirement(name = "Bearer Authentication") public class UserBoardController { diff --git a/global/src/main/java/com/homeaid/config/SwaggerConfig.java b/global/src/main/java/com/homeaid/config/SwaggerConfig.java index fb4fe0d0..ceab3710 100644 --- a/global/src/main/java/com/homeaid/config/SwaggerConfig.java +++ b/global/src/main/java/com/homeaid/config/SwaggerConfig.java @@ -1,7 +1,9 @@ package com.homeaid.config; +import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.security.SecurityRequirement; import io.swagger.v3.oas.models.security.SecurityScheme; import org.springdoc.core.models.GroupedOpenApi; import org.springframework.context.annotation.Bean; @@ -12,17 +14,16 @@ public class SwaggerConfig { @Bean public OpenAPI springOpenAPI() { - return new OpenAPI() .info(new Info() .title("HomeAid API 명세서") .version("v1") - .description("HomeAid는 매칭시스템을 활용하여 고객에게 맞춤형 매니저를 매칭시켜주고 가사 및 청소 서비스를 제공합니다.")); - /*// 전역 보안 요구사항 추가 + .description("HomeAid는 매칭시스템을 활용하여 고객에게 맞춤형 매니저를 매칭시켜주고 가사 및 청소 서비스를 제공합니다.")) + // 전역 보안 요구사항 추가 .addSecurityItem(new SecurityRequirement().addList("Bearer Authentication")) // 보안 스키마 정의 .components(new Components() - .addSecuritySchemes("Bearer Authentication", createBearerTokenScheme()));*/ + .addSecuritySchemes("Bearer Authentication", createBearerTokenScheme())); } /** @@ -83,12 +84,11 @@ public GroupedOpenApi worklogAPI() { } @Bean - public GroupedOpenApi boardAPI() { return GroupedOpenApi.builder() .group("Boards") - .displayName("💬 1:1 문의글") - .pathsToMatch("/api/v1/board/**") + .displayName("1:1 문의글") + .pathsToMatch("/api/v1/boards/**") .build(); } From fccf7488b6ec93c5dea579f484be005cbb395c5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Wed, 4 Jun 2025 01:25:22 +0900 Subject: [PATCH 02/20] =?UTF-8?q?feat:=20=EC=8A=A4=EC=9B=A8=EA=B1=B0=20Aut?= =?UTF-8?q?h=20=EA=B4=80=EB=A0=A8=20test=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/SwaggerAuthController.java | 98 +++++++++++++++++++ .../homeaid/controller/UserController.java | 14 +-- ...tDto.java => SwaggerSignInRequestDto.java} | 5 +- .../dto/response/SignInResponseDto.java | 16 +++ ...esponseDto.java => SignUpResponseDto.java} | 10 +- .../com/homeaid/exception/UserErrorCode.java | 5 +- .../SecurityAuthenticationFilter.java | 6 +- .../com/homeaid/security/SecurityConfig.java | 2 +- .../com/homeaid/service/UserServiceImpl.java | 16 +++ 9 files changed, 152 insertions(+), 20 deletions(-) create mode 100644 user/src/main/java/com/homeaid/controller/SwaggerAuthController.java rename user/src/main/java/com/homeaid/dto/request/{UserSignInRequestDto.java => SwaggerSignInRequestDto.java} (60%) create mode 100644 user/src/main/java/com/homeaid/dto/response/SignInResponseDto.java rename user/src/main/java/com/homeaid/dto/response/{UserSignUpResponseDto.java => SignUpResponseDto.java} (73%) diff --git a/user/src/main/java/com/homeaid/controller/SwaggerAuthController.java b/user/src/main/java/com/homeaid/controller/SwaggerAuthController.java new file mode 100644 index 00000000..c5d5c3d3 --- /dev/null +++ b/user/src/main/java/com/homeaid/controller/SwaggerAuthController.java @@ -0,0 +1,98 @@ +package com.homeaid.controller; + +import com.homeaid.common.response.CommonApiResponse; +import com.homeaid.domain.Customer; +import com.homeaid.domain.Manager; +import com.homeaid.dto.request.CustomerSignUpRequestDto; +import com.homeaid.dto.request.ManagerSignUpRequestDto; +import com.homeaid.dto.request.SwaggerSignInRequestDto; +import com.homeaid.dto.response.SignUpResponseDto; +import com.homeaid.dto.response.SignInResponseDto; +import com.homeaid.service.UserServiceImpl; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Profile; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/v1/swagger/auth") +@RequiredArgsConstructor +@Tag(name = "SignUp/SignIn", description = "사용자 회원가입 API (매니저/고객)") +//@Profile("swagger") // swagger 프로파일에서만 활성화 가능 (선택) +public class SwaggerAuthController { + + private final UserServiceImpl userServiceImpl; + private final BCryptPasswordEncoder passwordEncoder; + + @PostMapping("/signup/manager") + @Operation(summary = "매니저 회원가입", description = "매니저 사용자 정보를 입력받아 회원가입을 처리합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "201", description = "회원가입 성공" + , content = @Content(schema = @Schema(implementation = SignUpResponseDto.class))), + @ApiResponse(responseCode = "400", description = "잘못된 입력값" + , content = @Content(schema = @Schema(implementation = CommonApiResponse.class))), + @ApiResponse(responseCode = "409", description = "이미 존재하는 이메일" + , content = @Content(schema = @Schema(implementation = CommonApiResponse.class))) + }) + public ResponseEntity> signUpManager( + @RequestBody @Valid ManagerSignUpRequestDto managerSignUpRequestDto + ) { + + String password = passwordEncoder.encode(managerSignUpRequestDto.getPassword()); + Manager manager = userServiceImpl.signUpManager( + ManagerSignUpRequestDto.toEntity(managerSignUpRequestDto, password) // 비밀번호 추가 수정 필요 + ); + return ResponseEntity.status(HttpStatus.CREATED) + .body(CommonApiResponse.success(SignUpResponseDto.toManagerDto(manager))); + } + + @Operation(summary = "고객 회원가입", description = "고객 사용자 정보를 입력받아 회원가입을 처리합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "201", description = "회원가입 성공" + , content = @Content(schema = @Schema(implementation = SignUpResponseDto.class))), + @ApiResponse(responseCode = "400", description = "잘못된 입력값" + , content = @Content(schema = @Schema(implementation = CommonApiResponse.class))), + @ApiResponse(responseCode = "409", description = "이미 존재하는 이메일" + , content = @Content(schema = @Schema(implementation = CommonApiResponse.class))) + }) + @PostMapping("/signup/customer") + public ResponseEntity> signUpCustomer( + @RequestBody @Valid CustomerSignUpRequestDto customerSignUpRequestDto + ) { + + String password = passwordEncoder.encode(customerSignUpRequestDto.getPassword()); + + Customer customer = userServiceImpl.signUpCustomer( + CustomerSignUpRequestDto.toEntity(customerSignUpRequestDto, password) // 비밀번호 추가 수정 필요 + ); + return ResponseEntity.status(HttpStatus.CREATED) + .body(CommonApiResponse.success(SignUpResponseDto.toCustomerDto(customer))); + } + + @Operation(summary = "로그인", description = "이메일과 비밀번호를 받아 로그인을 처리합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "로그인 성공" + , content = @Content(schema = @Schema(implementation = SignUpResponseDto.class))), + @ApiResponse(responseCode = "400", description = "잘못된 입력값" + , content = @Content(schema = @Schema(implementation = CommonApiResponse.class))) + }) + @PostMapping("/signin") + public ResponseEntity swaggerSignIn( + @RequestBody SwaggerSignInRequestDto swaggerRequest) { + String token = userServiceImpl.loginAndGetToken(swaggerRequest); + return ResponseEntity.ok(new SignInResponseDto(token)); + } + +} diff --git a/user/src/main/java/com/homeaid/controller/UserController.java b/user/src/main/java/com/homeaid/controller/UserController.java index 664cf563..bbe94491 100644 --- a/user/src/main/java/com/homeaid/controller/UserController.java +++ b/user/src/main/java/com/homeaid/controller/UserController.java @@ -5,7 +5,7 @@ import com.homeaid.domain.Manager; import com.homeaid.dto.request.CustomerSignUpRequestDto; import com.homeaid.dto.request.ManagerSignUpRequestDto; -import com.homeaid.dto.response.UserSignUpResponseDto; +import com.homeaid.dto.response.SignUpResponseDto; import com.homeaid.service.UserService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; @@ -36,13 +36,13 @@ public class UserController { @Operation(summary = "매니저 회원가입", description = "매니저 사용자 정보를 입력받아 회원가입을 처리합니다.") @ApiResponses(value = { @ApiResponse(responseCode = "201", description = "회원가입 성공" - , content = @Content(schema = @Schema(implementation = UserSignUpResponseDto.class))), + , content = @Content(schema = @Schema(implementation = SignUpResponseDto.class))), @ApiResponse(responseCode = "400", description = "잘못된 입력값" , content = @Content(schema = @Schema(implementation = CommonApiResponse.class))), @ApiResponse(responseCode = "409", description = "이미 존재하는 이메일" , content = @Content(schema = @Schema(implementation = CommonApiResponse.class))) }) - public ResponseEntity> signUpManager( + public ResponseEntity> signUpManager( @RequestBody @Valid ManagerSignUpRequestDto managerSignUpRequestDto ) { @@ -51,20 +51,20 @@ public ResponseEntity> signUpManager( ManagerSignUpRequestDto.toEntity(managerSignUpRequestDto, password) // 비밀번호 추가 수정 필요 ); return ResponseEntity.status(HttpStatus.CREATED) - .body(CommonApiResponse.success(UserSignUpResponseDto.toManagerDto(manager))); + .body(CommonApiResponse.success(SignUpResponseDto.toManagerDto(manager))); } @Operation(summary = "고객 회원가입", description = "고객 사용자 정보를 입력받아 회원가입을 처리합니다.") @ApiResponses(value = { @ApiResponse(responseCode = "201", description = "회원가입 성공" - , content = @Content(schema = @Schema(implementation = UserSignUpResponseDto.class))), + , content = @Content(schema = @Schema(implementation = SignUpResponseDto.class))), @ApiResponse(responseCode = "400", description = "잘못된 입력값" , content = @Content(schema = @Schema(implementation = CommonApiResponse.class))), @ApiResponse(responseCode = "409", description = "이미 존재하는 이메일" , content = @Content(schema = @Schema(implementation = CommonApiResponse.class))) }) @PostMapping("/auth/signup/customer") - public ResponseEntity> signUpCustomer( + public ResponseEntity> signUpCustomer( @RequestBody @Valid CustomerSignUpRequestDto customerSignUpRequestDto ) { @@ -74,7 +74,7 @@ public ResponseEntity> signUpCustomer( CustomerSignUpRequestDto.toEntity(customerSignUpRequestDto, password) // 비밀번호 추가 수정 필요 ); return ResponseEntity.status(HttpStatus.CREATED) - .body(CommonApiResponse.success(UserSignUpResponseDto.toCustomerDto(customer))); + .body(CommonApiResponse.success(SignUpResponseDto.toCustomerDto(customer))); } } diff --git a/user/src/main/java/com/homeaid/dto/request/UserSignInRequestDto.java b/user/src/main/java/com/homeaid/dto/request/SwaggerSignInRequestDto.java similarity index 60% rename from user/src/main/java/com/homeaid/dto/request/UserSignInRequestDto.java rename to user/src/main/java/com/homeaid/dto/request/SwaggerSignInRequestDto.java index ff2ae5d5..92faa399 100644 --- a/user/src/main/java/com/homeaid/dto/request/UserSignInRequestDto.java +++ b/user/src/main/java/com/homeaid/dto/request/SwaggerSignInRequestDto.java @@ -1,17 +1,20 @@ package com.homeaid.dto.request; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotBlank; import lombok.Getter; @Getter -public class UserSignInRequestDto { +public class SwaggerSignInRequestDto { @Email(message = "올바른 이메일 형식이 아닙니다.") @NotBlank(message = "이메일은 필수 입력값입니다.") + @Schema(description = "이메일", example = "customer@example.com") private String email; @NotBlank(message = "비밀번호는 필수 입력값입니다.") + @Schema(description = "비밀번호 (8자 이상, 영문+숫자+특수문자 포함)", example = "Password1!") private String password; } diff --git a/user/src/main/java/com/homeaid/dto/response/SignInResponseDto.java b/user/src/main/java/com/homeaid/dto/response/SignInResponseDto.java new file mode 100644 index 00000000..a868d89b --- /dev/null +++ b/user/src/main/java/com/homeaid/dto/response/SignInResponseDto.java @@ -0,0 +1,16 @@ +package com.homeaid.dto.response; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class SignInResponseDto { + + private String token; + + public SignInResponseDto(String token) { + this.token = token; + } + +} diff --git a/user/src/main/java/com/homeaid/dto/response/UserSignUpResponseDto.java b/user/src/main/java/com/homeaid/dto/response/SignUpResponseDto.java similarity index 73% rename from user/src/main/java/com/homeaid/dto/response/UserSignUpResponseDto.java rename to user/src/main/java/com/homeaid/dto/response/SignUpResponseDto.java index 24e754ca..e4d743f4 100644 --- a/user/src/main/java/com/homeaid/dto/response/UserSignUpResponseDto.java +++ b/user/src/main/java/com/homeaid/dto/response/SignUpResponseDto.java @@ -10,7 +10,7 @@ @Getter @Builder @AllArgsConstructor -public class UserSignUpResponseDto { +public class SignUpResponseDto { @Schema(description = "가입된 이메일", example = "user@example.com") private String email; @@ -19,15 +19,15 @@ public class UserSignUpResponseDto { private String message; - public static UserSignUpResponseDto toManagerDto(Manager manager) { - return UserSignUpResponseDto.builder() + public static SignUpResponseDto toManagerDto(Manager manager) { + return SignUpResponseDto.builder() .email(manager.getEmail()) .message("회원가입이 완료되었습니다.") .build(); } - public static UserSignUpResponseDto toCustomerDto(Customer customer) { - return UserSignUpResponseDto.builder() + public static SignUpResponseDto toCustomerDto(Customer customer) { + return SignUpResponseDto.builder() .email(customer.getEmail()) .message("회원가입이 완료되었습니다.") .build(); diff --git a/user/src/main/java/com/homeaid/exception/UserErrorCode.java b/user/src/main/java/com/homeaid/exception/UserErrorCode.java index c2ca5079..01431a4f 100644 --- a/user/src/main/java/com/homeaid/exception/UserErrorCode.java +++ b/user/src/main/java/com/homeaid/exception/UserErrorCode.java @@ -16,10 +16,10 @@ public enum UserErrorCode implements BaseErrorCode { // 401 UNAUTHORIZED UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "UNAUTHORIZED", "로그인이 필요합니다."), - INVALID_CREDENTIALS(HttpStatus.UNAUTHORIZED, "INVALID_CREDENTIALS", "아이디 또는 비밀번호가 올바르지 않습니다."), + LOGIN_FAILED(HttpStatus.UNAUTHORIZED, "INVALID_CREDENTIALS", "아이디 또는 비밀번호가 올바르지 않습니다."), INVALID_TOKEN(HttpStatus.UNAUTHORIZED, "INVALID_TOKEN", "유효하지 않은 토큰입니다."), TOKEN_EXPIRED(HttpStatus.UNAUTHORIZED, "TOKEN_EXPIRED", "토큰이 만료되었습니다."), - USER_NOT_AUTHENTICATED(HttpStatus.UNAUTHORIZED, "USER_NOT_AUTHENTICATED", "인증되지 않은 사용자입니다."), + USER_NOT_AUTHENTICATED(HttpStatus.UNAUTHORIZED, "USER_NOT_AUTHENTICATED", "인증되지 않은 사용자입니다."), // 403 FORBIDDEN FORBIDDEN_USER_ACCESS(HttpStatus.FORBIDDEN, "FORBIDDEN_USER_ACCESS", "접근 권한이 없습니다."), @@ -29,7 +29,6 @@ public enum UserErrorCode implements BaseErrorCode { MANAGER_NOT_FOUND(HttpStatus.NOT_FOUND, "MANAGER_NOT_FOUND", "매니저를 찾을 수 없습니다."), CUSTOMER_NOT_FOUND(HttpStatus.NOT_FOUND, "CUSTOMER_NOT_FOUND", "고객을 찾을 수 없습니다."), - // 409 CONFLICT USER_ALREADY_EXISTS(HttpStatus.CONFLICT, "USER_ALREADY_EXISTS", "이미 가입된 회원입니다."), SOCIAL_ACCOUNT_ALREADY_LINKED(HttpStatus.CONFLICT, "SOCIAL_ACCOUNT_ALREADY_LINKED", "이미 연동된 소셜 계정입니다."), diff --git a/user/src/main/java/com/homeaid/security/SecurityAuthenticationFilter.java b/user/src/main/java/com/homeaid/security/SecurityAuthenticationFilter.java index a9bc5f53..82399d67 100644 --- a/user/src/main/java/com/homeaid/security/SecurityAuthenticationFilter.java +++ b/user/src/main/java/com/homeaid/security/SecurityAuthenticationFilter.java @@ -1,7 +1,7 @@ package com.homeaid.security; import com.fasterxml.jackson.databind.ObjectMapper; -import com.homeaid.dto.request.UserSignInRequestDto; +import com.homeaid.dto.request.SwaggerSignInRequestDto; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; @@ -25,8 +25,8 @@ public class SecurityAuthenticationFilter extends UsernamePasswordAuthentication public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { try { - UserSignInRequestDto requestDto = new ObjectMapper().readValue(request.getInputStream(), - UserSignInRequestDto.class); + SwaggerSignInRequestDto requestDto = new ObjectMapper().readValue(request.getInputStream(), + SwaggerSignInRequestDto.class); UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(requestDto.getEmail(), requestDto.getPassword()); diff --git a/user/src/main/java/com/homeaid/security/SecurityConfig.java b/user/src/main/java/com/homeaid/security/SecurityConfig.java index 6f989d0c..f9e0d3f6 100644 --- a/user/src/main/java/com/homeaid/security/SecurityConfig.java +++ b/user/src/main/java/com/homeaid/security/SecurityConfig.java @@ -36,7 +36,7 @@ SecurityFilterChain securityFilterChain(HttpSecurity http, AuthenticationManager http .authorizeHttpRequests((auth) -> auth - .requestMatchers("/api/v1/user/auth/signup/**").permitAll() + .requestMatchers("/api/v1/user/auth/signup/**", "/api/v1/swagger/auth/**").permitAll() .requestMatchers( "/swagger-ui/**", "/v3/api-docs/**", diff --git a/user/src/main/java/com/homeaid/service/UserServiceImpl.java b/user/src/main/java/com/homeaid/service/UserServiceImpl.java index c942ee7a..2d27ff2c 100644 --- a/user/src/main/java/com/homeaid/service/UserServiceImpl.java +++ b/user/src/main/java/com/homeaid/service/UserServiceImpl.java @@ -2,11 +2,14 @@ import com.homeaid.domain.Customer; import com.homeaid.domain.Manager; +import com.homeaid.dto.request.SwaggerSignInRequestDto; import com.homeaid.exception.CustomException; import com.homeaid.exception.UserErrorCode; import com.homeaid.repository.UserRepository; +import com.homeaid.security.JwtUtil; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; @Service @@ -14,6 +17,8 @@ public class UserServiceImpl implements UserService { private final UserRepository userRepository; + private final BCryptPasswordEncoder passwordEncoder; + private final JwtUtil jwtUtil; public Manager signUpManager(@Valid Manager manager) { @@ -36,4 +41,15 @@ public Customer signUpCustomer(Customer customer) { return customer; } + public String loginAndGetToken(SwaggerSignInRequestDto request) { + var user = userRepository.findByEmail(request.getEmail()) + .orElseThrow(() -> new CustomException(UserErrorCode.LOGIN_FAILED)); + + if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) { + throw new CustomException(UserErrorCode.LOGIN_FAILED); + } + + return jwtUtil.createJwt(user.getId(), user.getEmail(), user.getRole().name(), 1800000L); + } + } From 24a7e39c97bf8df437ff3f8fb6de958ea8057c00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Wed, 4 Jun 2025 10:34:15 +0900 Subject: [PATCH 03/20] =?UTF-8?q?feat:=20swagger=20JWT=20=EC=9D=B8?= =?UTF-8?q?=EC=A6=9D=20=EC=A0=81=EC=9A=A9=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- global/src/main/java/com/homeaid/config/SwaggerConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global/src/main/java/com/homeaid/config/SwaggerConfig.java b/global/src/main/java/com/homeaid/config/SwaggerConfig.java index ceab3710..bb3d5f79 100644 --- a/global/src/main/java/com/homeaid/config/SwaggerConfig.java +++ b/global/src/main/java/com/homeaid/config/SwaggerConfig.java @@ -70,7 +70,7 @@ public GroupedOpenApi userAPI() { return GroupedOpenApi.builder() .group("Users") - .pathsToMatch("/api/v1/user/**") + .pathsToMatch("/api/v1/swagger/auth/**") .build(); } From fb95e84a92b3896accdde1ad7f3cfbbe4ee477ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Wed, 4 Jun 2025 11:34:32 +0900 Subject: [PATCH 04/20] =?UTF-8?q?refactor:=20swagger=20displayName=20?= =?UTF-8?q?=EC=88=98=EC=A0=95,=20=EB=AC=B8=EC=9D=98=EA=B8=80=20=EC=A0=84?= =?UTF-8?q?=EC=B2=B4=20=EC=A1=B0=ED=9A=8C=20swagger=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/homeaid/controller/UserBoardController.java | 2 +- .../main/java/com/homeaid/config/SwaggerConfig.java | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/board/src/main/java/com/homeaid/controller/UserBoardController.java b/board/src/main/java/com/homeaid/controller/UserBoardController.java index 51dc86c9..38f21ab5 100644 --- a/board/src/main/java/com/homeaid/controller/UserBoardController.java +++ b/board/src/main/java/com/homeaid/controller/UserBoardController.java @@ -155,7 +155,7 @@ public ResponseEntity> getBoard( content = @Content(schema = @Schema(implementation = CommonApiResponse.class))) }) public ResponseEntity>> searchBoard( - @Parameter(description = "검색 키워드", example = "문의") + @Parameter(description = "검색 키워드") @RequestParam(name = "keyword", required = false) String keyword, @Parameter(description = "페이지 번호 (0부터 시작)", example = "0") @RequestParam(name = "page", defaultValue = "0") int page, diff --git a/global/src/main/java/com/homeaid/config/SwaggerConfig.java b/global/src/main/java/com/homeaid/config/SwaggerConfig.java index bb3d5f79..33b61580 100644 --- a/global/src/main/java/com/homeaid/config/SwaggerConfig.java +++ b/global/src/main/java/com/homeaid/config/SwaggerConfig.java @@ -43,6 +43,7 @@ private SecurityScheme createBearerTokenScheme() { public GroupedOpenApi reservationAPI() { return GroupedOpenApi.builder() .group("reservations") + .displayName("예약") .pathsToMatch("/api/v1/customer/reservations/**") .build(); } @@ -50,7 +51,8 @@ public GroupedOpenApi reservationAPI() { @Bean public GroupedOpenApi serviceOptionAPI() { return GroupedOpenApi.builder() - .group("serviceOption") + .group("serviceOptions") + .displayName("서비스 옵션") .pathsToMatch("/api/v1/admin/service-option/**") .build(); } @@ -60,6 +62,7 @@ public GroupedOpenApi serviceOptionAPI() { public GroupedOpenApi matchingAPI() { return GroupedOpenApi.builder() .group("matchings") + .displayName("매칭") .pathsToMatch("/api/v1/admin/matchings/**") .build(); } @@ -70,6 +73,7 @@ public GroupedOpenApi userAPI() { return GroupedOpenApi.builder() .group("Users") + .displayName("로그인/회원가입") .pathsToMatch("/api/v1/swagger/auth/**") .build(); } @@ -78,7 +82,8 @@ public GroupedOpenApi userAPI() { public GroupedOpenApi worklogAPI() { return GroupedOpenApi.builder() - .group("worklog") + .group("workLogs") + .displayName("작업기록") .pathsToMatch("/api/v1/manager/work-logs/**") .build(); } @@ -87,7 +92,7 @@ public GroupedOpenApi worklogAPI() { public GroupedOpenApi boardAPI() { return GroupedOpenApi.builder() .group("Boards") - .displayName("1:1 문의글") + .displayName("문의글") .pathsToMatch("/api/v1/boards/**") .build(); From 1ecbda421418ccac817c6b44913051798ce7f78e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Wed, 4 Jun 2025 13:24:19 +0900 Subject: [PATCH 05/20] =?UTF-8?q?fix:=20payment=20=EB=A8=B8=EC=A7=80=20?= =?UTF-8?q?=ED=9B=84=20=EC=97=90=EB=9F=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- reservation/src/main/java/com/homeaid/domain/Reservation.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/reservation/src/main/java/com/homeaid/domain/Reservation.java b/reservation/src/main/java/com/homeaid/domain/Reservation.java index 7dceb600..4f6392b9 100644 --- a/reservation/src/main/java/com/homeaid/domain/Reservation.java +++ b/reservation/src/main/java/com/homeaid/domain/Reservation.java @@ -48,6 +48,8 @@ public class Reservation { private Long customerId; + private Long managerId; + private Long finalMatchingId; private Double latitude; From 24fa74b670372922f02ee1de944aff4139fa71dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Wed, 4 Jun 2025 15:20:21 +0900 Subject: [PATCH 06/20] =?UTF-8?q?Response=20Dto=20=EC=9D=B4=EB=A6=84=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/homeaid/controller/SwaggerAuthController.java | 5 ++--- .../{SwaggerSignInRequestDto.java => SignInRequestDto.java} | 2 +- .../com/homeaid/security/SecurityAuthenticationFilter.java | 6 +++--- user/src/main/java/com/homeaid/service/UserServiceImpl.java | 4 ++-- 4 files changed, 8 insertions(+), 9 deletions(-) rename user/src/main/java/com/homeaid/dto/request/{SwaggerSignInRequestDto.java => SignInRequestDto.java} (94%) diff --git a/user/src/main/java/com/homeaid/controller/SwaggerAuthController.java b/user/src/main/java/com/homeaid/controller/SwaggerAuthController.java index c5d5c3d3..aa1960c2 100644 --- a/user/src/main/java/com/homeaid/controller/SwaggerAuthController.java +++ b/user/src/main/java/com/homeaid/controller/SwaggerAuthController.java @@ -5,7 +5,7 @@ import com.homeaid.domain.Manager; import com.homeaid.dto.request.CustomerSignUpRequestDto; import com.homeaid.dto.request.ManagerSignUpRequestDto; -import com.homeaid.dto.request.SwaggerSignInRequestDto; +import com.homeaid.dto.request.SignInRequestDto; import com.homeaid.dto.response.SignUpResponseDto; import com.homeaid.dto.response.SignInResponseDto; import com.homeaid.service.UserServiceImpl; @@ -17,7 +17,6 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; -import org.springframework.context.annotation.Profile; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; @@ -90,7 +89,7 @@ public ResponseEntity> signUpCustomer( }) @PostMapping("/signin") public ResponseEntity swaggerSignIn( - @RequestBody SwaggerSignInRequestDto swaggerRequest) { + @RequestBody SignInRequestDto swaggerRequest) { String token = userServiceImpl.loginAndGetToken(swaggerRequest); return ResponseEntity.ok(new SignInResponseDto(token)); } diff --git a/user/src/main/java/com/homeaid/dto/request/SwaggerSignInRequestDto.java b/user/src/main/java/com/homeaid/dto/request/SignInRequestDto.java similarity index 94% rename from user/src/main/java/com/homeaid/dto/request/SwaggerSignInRequestDto.java rename to user/src/main/java/com/homeaid/dto/request/SignInRequestDto.java index 92faa399..11ceb22c 100644 --- a/user/src/main/java/com/homeaid/dto/request/SwaggerSignInRequestDto.java +++ b/user/src/main/java/com/homeaid/dto/request/SignInRequestDto.java @@ -6,7 +6,7 @@ import lombok.Getter; @Getter -public class SwaggerSignInRequestDto { +public class SignInRequestDto { @Email(message = "올바른 이메일 형식이 아닙니다.") @NotBlank(message = "이메일은 필수 입력값입니다.") diff --git a/user/src/main/java/com/homeaid/security/SecurityAuthenticationFilter.java b/user/src/main/java/com/homeaid/security/SecurityAuthenticationFilter.java index 82399d67..e4f2bbe5 100644 --- a/user/src/main/java/com/homeaid/security/SecurityAuthenticationFilter.java +++ b/user/src/main/java/com/homeaid/security/SecurityAuthenticationFilter.java @@ -1,7 +1,7 @@ package com.homeaid.security; import com.fasterxml.jackson.databind.ObjectMapper; -import com.homeaid.dto.request.SwaggerSignInRequestDto; +import com.homeaid.dto.request.SignInRequestDto; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; @@ -25,8 +25,8 @@ public class SecurityAuthenticationFilter extends UsernamePasswordAuthentication public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { try { - SwaggerSignInRequestDto requestDto = new ObjectMapper().readValue(request.getInputStream(), - SwaggerSignInRequestDto.class); + SignInRequestDto requestDto = new ObjectMapper().readValue(request.getInputStream(), + SignInRequestDto.class); UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(requestDto.getEmail(), requestDto.getPassword()); diff --git a/user/src/main/java/com/homeaid/service/UserServiceImpl.java b/user/src/main/java/com/homeaid/service/UserServiceImpl.java index 2d27ff2c..f2b9d4ec 100644 --- a/user/src/main/java/com/homeaid/service/UserServiceImpl.java +++ b/user/src/main/java/com/homeaid/service/UserServiceImpl.java @@ -2,7 +2,7 @@ import com.homeaid.domain.Customer; import com.homeaid.domain.Manager; -import com.homeaid.dto.request.SwaggerSignInRequestDto; +import com.homeaid.dto.request.SignInRequestDto; import com.homeaid.exception.CustomException; import com.homeaid.exception.UserErrorCode; import com.homeaid.repository.UserRepository; @@ -41,7 +41,7 @@ public Customer signUpCustomer(Customer customer) { return customer; } - public String loginAndGetToken(SwaggerSignInRequestDto request) { + public String loginAndGetToken(SignInRequestDto request) { var user = userRepository.findByEmail(request.getEmail()) .orElseThrow(() -> new CustomException(UserErrorCode.LOGIN_FAILED)); From d9f1a46b74493768946b936cddced99bc052db65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Tue, 3 Jun 2025 19:45:18 +0900 Subject: [PATCH 07/20] =?UTF-8?q?refactor:=20'=EC=82=AC=EC=9A=A9=EC=9E=90?= =?UTF-8?q?=20=EB=A6=AC=EB=B7=B0'=20pr=20=EC=BD=94=EB=93=9C=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EA=B8=B0=EB=B0=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/homeaid/config/SwaggerConfig.java | 2 +- .../com/homeaid/controller/ReviewController.java | 5 +++-- .../dto/request/CustomerReviewRequestDto.java | 15 +++++++++++++-- .../com/homeaid/service/ReviewServiceImpl.java | 2 +- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/global/src/main/java/com/homeaid/config/SwaggerConfig.java b/global/src/main/java/com/homeaid/config/SwaggerConfig.java index 33b61580..0b0d8f92 100644 --- a/global/src/main/java/com/homeaid/config/SwaggerConfig.java +++ b/global/src/main/java/com/homeaid/config/SwaggerConfig.java @@ -103,7 +103,7 @@ public GroupedOpenApi reviewAPI() { return GroupedOpenApi.builder() .group("reviews") .displayName("리뷰") - .pathsToMatch("/api/v1/review/**") + .pathsToMatch("/api/v1/reviews/**") .build(); } } diff --git a/review/src/main/java/com/homeaid/controller/ReviewController.java b/review/src/main/java/com/homeaid/controller/ReviewController.java index 24205eb5..48669ef8 100644 --- a/review/src/main/java/com/homeaid/controller/ReviewController.java +++ b/review/src/main/java/com/homeaid/controller/ReviewController.java @@ -7,6 +7,7 @@ import com.homeaid.security.CustomUserDetails; import com.homeaid.service.ReviewService; import io.swagger.v3.oas.annotations.Operation; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; @@ -14,7 +15,7 @@ @RequiredArgsConstructor @RestController -@RequestMapping("/api/v1/review") +@RequestMapping("/api/v1/reviews") public class ReviewController { private final ReviewService reviewService; @@ -23,7 +24,7 @@ public class ReviewController { @PostMapping public ResponseEntity> createReview( @AuthenticationPrincipal CustomUserDetails user, - @RequestBody CustomerReviewRequestDto customerReviewRequestDto) { + @RequestBody @Valid CustomerReviewRequestDto customerReviewRequestDto) { Review requestReview = CustomerReviewRequestDto.toEntity(customerReviewRequestDto, user.getUserRole(), user.getUserId()); diff --git a/review/src/main/java/com/homeaid/dto/request/CustomerReviewRequestDto.java b/review/src/main/java/com/homeaid/dto/request/CustomerReviewRequestDto.java index 8e63bcbc..51401a6b 100644 --- a/review/src/main/java/com/homeaid/dto/request/CustomerReviewRequestDto.java +++ b/review/src/main/java/com/homeaid/dto/request/CustomerReviewRequestDto.java @@ -3,21 +3,32 @@ import com.homeaid.domain.Review; import com.homeaid.domain.enumerate.UserRole; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; import lombok.Getter; @Getter public class CustomerReviewRequestDto { + @NotNull(message = "리뷰 대상자는 필수입니다") private Long targetId; - private Integer rating; + @NotNull(message = "평점은 필수입니다") + @Min(value = 1, message = "평점은 1점 이상이어야 합니다") + @Max(value = 5, message = "평점은 5점 이하여야 합니다") + private int rating; + @NotBlank(message = "리뷰 내용은 필수입니다") + @Size(min = 10, message = "리뷰는 10자 이상 작성해주세요") private String comment; + @NotNull(message = "예약 ID는 필수입니다") private Long reservationId; public static Review toEntity(CustomerReviewRequestDto customerReviewRequestDto, UserRole userRole, Long userId) { - return Review.builder() .writerId(userId) .targetId(customerReviewRequestDto.targetId) diff --git a/review/src/main/java/com/homeaid/service/ReviewServiceImpl.java b/review/src/main/java/com/homeaid/service/ReviewServiceImpl.java index 9cf3b58e..d70f7084 100644 --- a/review/src/main/java/com/homeaid/service/ReviewServiceImpl.java +++ b/review/src/main/java/com/homeaid/service/ReviewServiceImpl.java @@ -69,7 +69,7 @@ public void deleteReview(Long reviewId, Long userId) { throw new CustomException(ReviewErrorCode.UNAUTHORIZED_REVIEW_ACCESS); } - reservationRepository.deleteById(reviewId); + reviewRepository.deleteById(reviewId); } From 93b1c539514f3148eb26521153783b68cb9a0a2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Tue, 3 Jun 2025 20:24:52 +0900 Subject: [PATCH 08/20] =?UTF-8?q?feat:=20=EB=A7=A4=EB=8B=88=EC=A0=80,=20?= =?UTF-8?q?=EA=B3=A0=EA=B0=9D=20=ED=86=B5=EA=B3=84=20Entity=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/homeaid/domain/CustomerRating.java | 53 +++++++++++++++++++ .../com/homeaid/domain/ManagerRating.java | 53 +++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 user/src/main/java/com/homeaid/domain/CustomerRating.java create mode 100644 user/src/main/java/com/homeaid/domain/ManagerRating.java diff --git a/user/src/main/java/com/homeaid/domain/CustomerRating.java b/user/src/main/java/com/homeaid/domain/CustomerRating.java new file mode 100644 index 00000000..2cdb7311 --- /dev/null +++ b/user/src/main/java/com/homeaid/domain/CustomerRating.java @@ -0,0 +1,53 @@ +package com.homeaid.domain; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.MapsId; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; +import java.time.LocalDateTime; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.LastModifiedDate; + +@Entity +@Getter +@Table(name = "customer_rating") +@NoArgsConstructor +public class CustomerRating { + + @Id + @Column(name = "customer_id") + private Long customerId; + + @Column(name = "review_count", nullable = false) + private Integer reviewCount; + + @Column(name = "average_rating", nullable = false) + private Double averageRating; + + @Column(name = "last_updated", nullable = false) + @LastModifiedDate + private LocalDateTime lastUpdated; + + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "customer_id") + @MapsId // customer_id 를 PK로 사용 + private Customer customer; + + public CustomerRating(Customer customer){ + this.customerId = customer.getId(); + this.reviewCount = 0; + this.averageRating = 0.0; + this.customer = customer; + } + + public void updateRating(Integer reviewCount, Double averageRating){ + this.averageRating = averageRating; + this.reviewCount = reviewCount; + } + +} diff --git a/user/src/main/java/com/homeaid/domain/ManagerRating.java b/user/src/main/java/com/homeaid/domain/ManagerRating.java new file mode 100644 index 00000000..deb3a9f6 --- /dev/null +++ b/user/src/main/java/com/homeaid/domain/ManagerRating.java @@ -0,0 +1,53 @@ +package com.homeaid.domain; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.MapsId; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; +import java.time.LocalDateTime; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.LastModifiedDate; + +@Entity +@Getter +@Table(name = "manager_rating") +@NoArgsConstructor +public class ManagerRating { + + @Id + @Column(name = "manager_id") + private Long managerId; + + @Column(name = "review_count", nullable = false) + private Integer reviewCount; + + @Column(name = "average_rating", nullable = false) + private Double averageRating; + + @Column(name = "last_updated", nullable = false) + @LastModifiedDate + private LocalDateTime lastUpdated; + + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "manager_id") + @MapsId // manager_id 를 PK로 사용 + private Manager manager; + + public ManagerRating(Manager manager){ + this.managerId = manager.getId(); + this.reviewCount = 0; + this.averageRating = 0.0; + this.manager = manager; + } + + public void updateRating(Integer reviewCount, double averageRating) { + this.averageRating = averageRating; + this.reviewCount = reviewCount; + } + +} From 7ab5da8d9beed0cbf7dd7ebda6c7b2e914eaafea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Tue, 3 Jun 2025 20:25:35 +0900 Subject: [PATCH 09/20] =?UTF-8?q?feat:=20=EB=A7=A4=EB=8B=88=EC=A0=80,=20?= =?UTF-8?q?=EA=B3=A0=EA=B0=9D=20=ED=86=B5=EA=B3=84=20Repository=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../homeaid/repository/CustomerRatingRepository.java | 10 ++++++++++ .../homeaid/repository/ManagerRatingRepository.java | 10 ++++++++++ 2 files changed, 20 insertions(+) create mode 100644 user/src/main/java/com/homeaid/repository/CustomerRatingRepository.java create mode 100644 user/src/main/java/com/homeaid/repository/ManagerRatingRepository.java diff --git a/user/src/main/java/com/homeaid/repository/CustomerRatingRepository.java b/user/src/main/java/com/homeaid/repository/CustomerRatingRepository.java new file mode 100644 index 00000000..961301ea --- /dev/null +++ b/user/src/main/java/com/homeaid/repository/CustomerRatingRepository.java @@ -0,0 +1,10 @@ +package com.homeaid.repository; + +import com.homeaid.domain.CustomerRating; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface CustomerRatingRepository extends JpaRepository { + +} diff --git a/user/src/main/java/com/homeaid/repository/ManagerRatingRepository.java b/user/src/main/java/com/homeaid/repository/ManagerRatingRepository.java new file mode 100644 index 00000000..83e1b5c6 --- /dev/null +++ b/user/src/main/java/com/homeaid/repository/ManagerRatingRepository.java @@ -0,0 +1,10 @@ +package com.homeaid.repository; + +import com.homeaid.domain.ManagerRating; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface ManagerRatingRepository extends JpaRepository { + +} From edb8a2d2f80836adc45c86c66427596fdcb27ae1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Tue, 3 Jun 2025 21:48:08 +0900 Subject: [PATCH 10/20] =?UTF-8?q?feat:=20=EB=A6=AC=EB=B7=B0=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20=EB=B0=8F=20=EC=82=AD=EC=A0=9C=20=EC=8B=9C,=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=EC=9E=90=20=ED=86=B5=EA=B3=84=20update?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/homeaid/domain/Review.java | 2 +- .../homeaid/repository/ReviewRepository.java | 7 +++ .../homeaid/service/ReviewServiceImpl.java | 12 +++- .../service/UserRatingUpdateService.java | 9 +++ .../service/UserRatingUpdateServiceImpl.java | 58 +++++++++++++++++++ .../com/homeaid/domain/CustomerRating.java | 2 +- .../com/homeaid/domain/ManagerRating.java | 2 +- 7 files changed, 87 insertions(+), 5 deletions(-) create mode 100644 review/src/main/java/com/homeaid/service/UserRatingUpdateService.java create mode 100644 review/src/main/java/com/homeaid/service/UserRatingUpdateServiceImpl.java diff --git a/review/src/main/java/com/homeaid/domain/Review.java b/review/src/main/java/com/homeaid/domain/Review.java index d62e8b56..d76e31bc 100644 --- a/review/src/main/java/com/homeaid/domain/Review.java +++ b/review/src/main/java/com/homeaid/domain/Review.java @@ -23,7 +23,7 @@ public class Review { private Long targetId; - private int rating; //별점 + private Integer rating; //별점 private String comment; diff --git a/review/src/main/java/com/homeaid/repository/ReviewRepository.java b/review/src/main/java/com/homeaid/repository/ReviewRepository.java index 1a999afc..7e613749 100644 --- a/review/src/main/java/com/homeaid/repository/ReviewRepository.java +++ b/review/src/main/java/com/homeaid/repository/ReviewRepository.java @@ -2,9 +2,16 @@ import com.homeaid.domain.Review; +import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; @Repository public interface ReviewRepository extends JpaRepository { + + @Query("SELECT COUNT(r), AVG(r.rating) FROM Review r WHERE r.targetId = :targetId") + Object[] getReviewStatisticsByTargetId(@Param("targetId") Long targetId); + } diff --git a/review/src/main/java/com/homeaid/service/ReviewServiceImpl.java b/review/src/main/java/com/homeaid/service/ReviewServiceImpl.java index d70f7084..0b4f07b2 100644 --- a/review/src/main/java/com/homeaid/service/ReviewServiceImpl.java +++ b/review/src/main/java/com/homeaid/service/ReviewServiceImpl.java @@ -20,6 +20,7 @@ public class ReviewServiceImpl implements ReviewService { private final ReviewRepository reviewRepository; private final ReservationRepository reservationRepository; private final MatchingRepository matchingRepository; + private final UserRatingUpdateService userRatingUpdateService; @Transactional @Override @@ -37,9 +38,12 @@ public Review createReviewByCustomer(Review requestReview) { throw new CustomException(ReviewErrorCode.UNAUTHORIZED_REVIEW_TARGET); } + Review savedReview = reviewRepository.save(requestReview); + userRatingUpdateService.updateRating(requestReview.getTargetId(), requestReview.getWriterRole()); + //Todo 매니저 찜 기능 - return reviewRepository.save(requestReview); + return savedReview; } @Transactional @@ -58,7 +62,10 @@ public Review createReviewByManager(Review requestReview) { throw new CustomException(ReviewErrorCode.UNAUTHORIZED_REVIEW_TARGET); } - return reviewRepository.save(requestReview); + Review savedReview = reviewRepository.save(requestReview); + userRatingUpdateService.updateRating(requestReview.getTargetId(), requestReview.getWriterRole()); + + return savedReview; } public void deleteReview(Long reviewId, Long userId) { @@ -70,6 +77,7 @@ public void deleteReview(Long reviewId, Long userId) { } reviewRepository.deleteById(reviewId); + userRatingUpdateService.updateRating(review.getTargetId(), review.getWriterRole()); } diff --git a/review/src/main/java/com/homeaid/service/UserRatingUpdateService.java b/review/src/main/java/com/homeaid/service/UserRatingUpdateService.java new file mode 100644 index 00000000..996f882b --- /dev/null +++ b/review/src/main/java/com/homeaid/service/UserRatingUpdateService.java @@ -0,0 +1,9 @@ +package com.homeaid.service; + +import com.homeaid.domain.enumerate.UserRole; + +public interface UserRatingUpdateService { + + void updateRating(Long targetId, UserRole writerRole); + +} diff --git a/review/src/main/java/com/homeaid/service/UserRatingUpdateServiceImpl.java b/review/src/main/java/com/homeaid/service/UserRatingUpdateServiceImpl.java new file mode 100644 index 00000000..ccdcf48d --- /dev/null +++ b/review/src/main/java/com/homeaid/service/UserRatingUpdateServiceImpl.java @@ -0,0 +1,58 @@ +package com.homeaid.service; + +import com.homeaid.domain.Customer; +import com.homeaid.domain.CustomerRating; +import com.homeaid.domain.Manager; +import com.homeaid.domain.ManagerRating; +import com.homeaid.domain.enumerate.UserRole; +import com.homeaid.repository.CustomerRatingRepository; +import com.homeaid.repository.CustomerRepository; +import com.homeaid.repository.ManagerRatingRepository; +import com.homeaid.repository.ManagerRepository; +import com.homeaid.repository.ReviewRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class UserRatingUpdateServiceImpl implements UserRatingUpdateService { + + private final ReviewRepository reviewRepository; + private final CustomerRepository customerRepository; + private final ManagerRepository managerRepository; + private final CustomerRatingRepository customerRatingRepository; + private final ManagerRatingRepository managerRatingRepository; + + @Transactional + @Override + public void updateRating(Long targetId, UserRole writerRole) { + Object[] rating = reviewRepository.getReviewStatisticsByTargetId(targetId); + + int count = ((Number) rating[0]).intValue(); + double average = ((Number) rating[1]).doubleValue(); + + // 작성자: CUSTOMER -> MANAGER update / 작성자: MANAGER -> CUSTOMER update + if (writerRole == UserRole.CUSTOMER) { + updateManagerRating(targetId, count, average); + } else if (writerRole == UserRole.MANAGER) { + updateCustomerRating(targetId, count, average); + } + } + + private void updateManagerRating(Long managerId, int count, double average) { + Manager manager = managerRepository.getReferenceById(managerId); + ManagerRating rating = managerRatingRepository.findById(managerId) + .orElseGet(() -> new ManagerRating(manager)); + rating.updateRating(count, average); + managerRatingRepository.save(rating); + } + + private void updateCustomerRating(Long customerId, int count, double average) { + Customer customer = customerRepository.getReferenceById(customerId); + CustomerRating rating = customerRatingRepository.findById(customerId) + .orElseGet(() -> new CustomerRating(customer)); + rating.updateRating(count, average); + customerRatingRepository.save(rating); + } +} diff --git a/user/src/main/java/com/homeaid/domain/CustomerRating.java b/user/src/main/java/com/homeaid/domain/CustomerRating.java index 2cdb7311..a45657b5 100644 --- a/user/src/main/java/com/homeaid/domain/CustomerRating.java +++ b/user/src/main/java/com/homeaid/domain/CustomerRating.java @@ -45,7 +45,7 @@ public CustomerRating(Customer customer){ this.customer = customer; } - public void updateRating(Integer reviewCount, Double averageRating){ + public void updateRating(int reviewCount, double averageRating){ this.averageRating = averageRating; this.reviewCount = reviewCount; } diff --git a/user/src/main/java/com/homeaid/domain/ManagerRating.java b/user/src/main/java/com/homeaid/domain/ManagerRating.java index deb3a9f6..2d52c288 100644 --- a/user/src/main/java/com/homeaid/domain/ManagerRating.java +++ b/user/src/main/java/com/homeaid/domain/ManagerRating.java @@ -40,7 +40,7 @@ public class ManagerRating { public ManagerRating(Manager manager){ this.managerId = manager.getId(); - this.reviewCount = 0; + this.reviewCount = 0 ; this.averageRating = 0.0; this.manager = manager; } From 20146ef56d65365fea98911ece796549e0eee963 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Tue, 3 Jun 2025 22:15:51 +0900 Subject: [PATCH 11/20] =?UTF-8?q?refactor:=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20import=20=EC=A0=9C=EA=B1=B0=20?= =?UTF-8?q?=EB=B0=8F=20=EC=BD=94=EB=93=9C=20=EC=A0=95=EB=A0=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../homeaid/repository/ReviewRepository.java | 1 - .../homeaid/service/ReviewServiceImpl.java | 138 +++++++++--------- 2 files changed, 72 insertions(+), 67 deletions(-) diff --git a/review/src/main/java/com/homeaid/repository/ReviewRepository.java b/review/src/main/java/com/homeaid/repository/ReviewRepository.java index 7e613749..ef9b056f 100644 --- a/review/src/main/java/com/homeaid/repository/ReviewRepository.java +++ b/review/src/main/java/com/homeaid/repository/ReviewRepository.java @@ -2,7 +2,6 @@ import com.homeaid.domain.Review; -import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; diff --git a/review/src/main/java/com/homeaid/service/ReviewServiceImpl.java b/review/src/main/java/com/homeaid/service/ReviewServiceImpl.java index 0b4f07b2..61582d80 100644 --- a/review/src/main/java/com/homeaid/service/ReviewServiceImpl.java +++ b/review/src/main/java/com/homeaid/service/ReviewServiceImpl.java @@ -17,89 +17,95 @@ @RequiredArgsConstructor @Service public class ReviewServiceImpl implements ReviewService { - private final ReviewRepository reviewRepository; - private final ReservationRepository reservationRepository; - private final MatchingRepository matchingRepository; - private final UserRatingUpdateService userRatingUpdateService; - @Transactional - @Override - public Review createReviewByCustomer(Review requestReview) { - Reservation validatedReservation = validateReview(requestReview); + private final ReviewRepository reviewRepository; + private final ReservationRepository reservationRepository; + private final MatchingRepository matchingRepository; + private final UserRatingUpdateService userRatingUpdateService; - //예약건의 고객아이디와 요청 고객의 아이디 검증 - if (!validatedReservation.getCustomerId().equals(requestReview.getWriterId())) { - throw new CustomException(ReviewErrorCode.UNAUTHORIZED_REVIEW_ACCESS); - } + @Transactional + @Override + public Review createReviewByCustomer(Review requestReview) { + Reservation validatedReservation = validateReview(requestReview); - //타켓 매니저 와 예약 건의 매니저 검증 - Long reservationManagerId = getFinalMatchingOfManagerId(validatedReservation.getFinalMatchingId()); - if (!reservationManagerId.equals(requestReview.getTargetId())) { - throw new CustomException(ReviewErrorCode.UNAUTHORIZED_REVIEW_TARGET); - } - - Review savedReview = reviewRepository.save(requestReview); - userRatingUpdateService.updateRating(requestReview.getTargetId(), requestReview.getWriterRole()); - - //Todo 매니저 찜 기능 + //예약건의 고객아이디와 요청 고객의 아이디 검증 + if (!validatedReservation.getCustomerId().equals(requestReview.getWriterId())) { + throw new CustomException(ReviewErrorCode.UNAUTHORIZED_REVIEW_ACCESS); + } - return savedReview; + //타켓 매니저 와 예약 건의 매니저 검증 + Long reservationManagerId = getFinalMatchingOfManagerId( + validatedReservation.getFinalMatchingId()); + if (!reservationManagerId.equals(requestReview.getTargetId())) { + throw new CustomException(ReviewErrorCode.UNAUTHORIZED_REVIEW_TARGET); } - @Transactional - @Override - public Review createReviewByManager(Review requestReview) { - Reservation validatedReservation = validateReview(requestReview); + Review savedReview = reviewRepository.save(requestReview); + userRatingUpdateService.updateRating(requestReview.getTargetId(), + requestReview.getWriterRole()); + + //Todo 매니저 찜 기능 - //예약 건의 매니저와 와 요청자의 매니저 아이디 검증 - Long reservationManagerId = getFinalMatchingOfManagerId(validatedReservation.getFinalMatchingId()); - if (!reservationManagerId.equals(requestReview.getWriterId())) { - throw new CustomException(ReviewErrorCode.UNAUTHORIZED_REVIEW_ACCESS); - } + return savedReview; + } - //예약 건의 고객아이디와 요청 받은 고객아이디 검증 - if (!validatedReservation.getCustomerId().equals(requestReview.getTargetId())) { - throw new CustomException(ReviewErrorCode.UNAUTHORIZED_REVIEW_TARGET); - } + @Transactional + @Override + public Review createReviewByManager(Review requestReview) { + Reservation validatedReservation = validateReview(requestReview); - Review savedReview = reviewRepository.save(requestReview); - userRatingUpdateService.updateRating(requestReview.getTargetId(), requestReview.getWriterRole()); + //예약 건의 매니저와 와 요청자의 매니저 아이디 검증 + Long reservationManagerId = getFinalMatchingOfManagerId( + validatedReservation.getFinalMatchingId()); + if (!reservationManagerId.equals(requestReview.getWriterId())) { + throw new CustomException(ReviewErrorCode.UNAUTHORIZED_REVIEW_ACCESS); + } - return savedReview; + //예약 건의 고객아이디와 요청 받은 고객아이디 검증 + if (!validatedReservation.getCustomerId().equals(requestReview.getTargetId())) { + throw new CustomException(ReviewErrorCode.UNAUTHORIZED_REVIEW_TARGET); } - public void deleteReview(Long reviewId, Long userId) { - Review review = reviewRepository.findById(reviewId).orElseThrow(() -> - new CustomException(ReviewErrorCode.REVIEW_NOT_FOUND)); + Review savedReview = reviewRepository.save(requestReview); + userRatingUpdateService.updateRating(requestReview.getTargetId(), + requestReview.getWriterRole()); + + return savedReview; + } - if (!review.getWriterId().equals(userId)) { - throw new CustomException(ReviewErrorCode.UNAUTHORIZED_REVIEW_ACCESS); - } + public void deleteReview(Long reviewId, Long userId) { + Review review = reviewRepository.findById(reviewId).orElseThrow(() -> + new CustomException(ReviewErrorCode.REVIEW_NOT_FOUND)); - reviewRepository.deleteById(reviewId); - userRatingUpdateService.updateRating(review.getTargetId(), review.getWriterRole()); + if (!review.getWriterId().equals(userId)) { + throw new CustomException(ReviewErrorCode.UNAUTHORIZED_REVIEW_ACCESS); } + reviewRepository.deleteById(reviewId); + userRatingUpdateService.updateRating(review.getTargetId(), review.getWriterRole()); + } - /** - * 요청 받은 예약이 유요한 예약이고 완료상태 검증 - * - * @param requestReview 요청한예약 아이디 - * @return 조회된 예약 - */ - private Reservation validateReview(Review requestReview) { - Reservation reservation = reservationRepository.findById(requestReview.getReservationId()).orElseThrow(() -> - new CustomException(ReservationErrorCode.RESERVATION_NOT_FOUND)); - - if (reservation.getStatus() != ReservationStatus.COMPLETED) { - throw new CustomException(ReviewErrorCode.REVIEW_NOT_ALLOWED); - } - return reservation; - } - private Long getFinalMatchingOfManagerId(Long finalMatchingId) { - return matchingRepository.findById(finalMatchingId).orElseThrow( - () -> new CustomException(MatchingErrorCode.MATCHING_NOT_FOUND) - ).getManager().getId(); + /** + * 요청 받은 예약이 유요한 예약이고 완료상태 검증 + * + * @param requestReview 요청한예약 아이디 + * @return 조회된 예약 + */ + private Reservation validateReview(Review requestReview) { + Reservation reservation = reservationRepository.findById(requestReview.getReservationId()) + .orElseThrow(() -> + new CustomException(ReservationErrorCode.RESERVATION_NOT_FOUND)); + + if (reservation.getStatus() != ReservationStatus.COMPLETED) { + throw new CustomException(ReviewErrorCode.REVIEW_NOT_ALLOWED); } + return reservation; + } + + private Long getFinalMatchingOfManagerId(Long finalMatchingId) { + return matchingRepository.findById(finalMatchingId).orElseThrow( + () -> new CustomException(MatchingErrorCode.MATCHING_NOT_FOUND) + ).getManager().getId(); + } } From df34f33d59beae4074a0cdb3f4e9b49f30b06145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=83=81=EC=9A=B0=EC=A7=84=20=28Sang=20Woo=20Jin=29?= <80154292+SangWJDev@users.noreply.github.com> Date: Tue, 3 Jun 2025 15:38:49 +0900 Subject: [PATCH 12/20] Delete .github/workflows/CICD.yml --- .github/workflows/CICD.yml | 96 -------------------------------------- 1 file changed, 96 deletions(-) delete mode 100644 .github/workflows/CICD.yml diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml deleted file mode 100644 index 560e1a5a..00000000 --- a/.github/workflows/CICD.yml +++ /dev/null @@ -1,96 +0,0 @@ -name: Backend CI - -on: - push: - branches: [ dev ] - pull_request: - branches: [ dev ] - pull_request_target: - branches: [ dev ] # fork PR secrets 사용 가능 - types: [opened, synchronize, reopened] - -jobs: - build-and-test: - runs-on: ubuntu-latest - if: | - (github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != github.repository) || - (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) || - github.event_name == 'push' - - services: - mysql: - image: mysql:latest - env: - MYSQL_DATABASE: ${{ secrets.DB_NAME }} - MYSQL_ROOT_PASSWORD: root - MYSQL_USER: homeaid_user - MYSQL_PASSWORD: ${{ secrets.DB_PASSWORD }} - ports: - - 3306:3306 - options: >- - --health-cmd="mysqladmin ping -h localhost --silent" - --health-interval=10s - --health-timeout=5s - --health-retries=3 - - steps: - - name: Checkout PR code (fork PR only) - if: github.event_name == 'pull_request_target' - uses: actions/checkout@v3 - with: - ref: ${{ github.event.pull_request.head.ref }} - repository: ${{ github.event.pull_request.head.repo.full_name }} - - - name: Checkout code (for push or internal pull_request) - if: github.event_name != 'pull_request_target' - uses: actions/checkout@v3 - - - name: Set up JDK 17 (corretto) - uses: actions/setup-java@v3 - with: - java-version: '17' - distribution: 'corretto' - cache: gradle # 👉 캐시 추가: 빌드 속도 향상 - - - name: Grant execute permission for gradlew - run: chmod +x gradlew - - - name: Build and Test - env: - DB_DRIVER: ${{ secrets.DB_DRIVER }} - DB_HOST: ${{ secrets.DB_HOST }} - DB_PORT: ${{ secrets.DB_PORT }} - DB_NAME: ${{ secrets.DB_NAME }} - DB_USERNAME: ${{ secrets.DB_USERNAME }} - DB_PASSWORD: ${{ secrets.DB_PASSWORD }} - run: ./gradlew clean build - - - name: Notify success on Discord - if: success() - run: | - curl -H "Content-Type: application/json" \ - -X POST \ - -d '{ - "embeds": [{ - "title": "✅ CI 성공", - "description": "**📦 Repository:** `${{ github.repository }}`\n**🌿 Branch:** `${{ github.ref_name }}`\n**🧪 Workflow:** `${{ github.workflow }}`\n**🎯 Event:** `${{ github.event_name }}`\n**👤 작성자:** `${{ github.actor }}`\n\n[🔗 GitHub Actions 로그 확인하기](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})\n\n${{ github.event_name == 'pull_request' && format('[🔍 PR 보기](https://github.com/{0}/pull/{1})', github.repository, github.event.pull_request.number) || '' }}", - "color": 5763719 - }], - "content": "✅ CI 통과: `${{ github.ref_name }}` 브랜치입니다!" - }' \ - ${{ secrets.DISCORD_WEBHOOK }} - - - name: Notify failure on Discord - if: failure() - run: | - curl -H "Content-Type: application/json" \ - -X POST \ - -d '{ - "embeds": [{ - "title": "❌ CI 실패", - "description": "**📦 Repository:** `${{ github.repository }}`\n**🌿 Branch:** `${{ github.ref_name }}`\n**🧪 Workflow:** `${{ github.workflow }}`\n**🎯 Event:** `${{ github.event_name }}`\n**👤 작성자:** `${{ github.actor }}`\n\n[🔗 GitHub Actions 로그 확인하기](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})\n\n${{ github.event_name == 'pull_request' && format('[🔍 PR 보기](https://github.com/{0}/pull/{1})', github.repository, github.event.pull_request.number) || '' }}", - "color": 16711680 - }], - "content": "❗ CI 실패 발생: `${{ github.ref_name }}` 브랜치 확인해주세요!" - }' \ - ${{ secrets.DISCORD_WEBHOOK }} From 6fbb6b44fa4ed47a44dac9a17f5e5b392f6e73d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=83=81=EC=9A=B0=EC=A7=84=20=28Sang=20Woo=20Jin=29?= <80154292+SangWJDev@users.noreply.github.com> Date: Wed, 4 Jun 2025 09:11:07 +0900 Subject: [PATCH 13/20] Create docker-compose.yml --- docker-compose.yml | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 docker-compose.yml diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..7b9b2937 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,36 @@ +version: '3.8' + +services: + jenkins: + image: jenkins/jenkins:lts + container_name: jenkins + ports: + - "8080:8080" + - "50000:50000" + volumes: + - jenkins_home:/var/jenkins_home + networks: + - ci-network + + mysql-ci: + image: mysql:latest + container_name: mysql-ci + ports: + - "3306:3306" + environment: + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: homeaid_db + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] + interval: 30s + timeout: 10s + retries: 5 + networks: + - ci-network + +volumes: + jenkins_home: + +networks: + ci-network: + driver: bridge From a6417400ac4218b41ea30e89947349c63edbc930 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=83=81=EC=9A=B0=EC=A7=84=20=28Sang=20Woo=20Jin=29?= <80154292+SangWJDev@users.noreply.github.com> Date: Wed, 4 Jun 2025 09:15:33 +0900 Subject: [PATCH 14/20] Update Jenkinsfile --- Jenkinsfile | 38 ++++++-------------------------------- 1 file changed, 6 insertions(+), 32 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index a67d7c27..0a1aac3b 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -3,6 +3,12 @@ pipeline { environment { DISCORD_WEBHOOK = credentials('discord-webhook') + DB_DRIVER = 'mysql' + DB_HOST = 'mysql-ci' // Docker Compose 서비스명 (같은 네트워크) + DB_PORT = '3306' + DB_NAME = 'homeaid_db' + DB_USERNAME = 'homeaid_user' + DB_PASSWORD = 'root' } tools { @@ -16,38 +22,6 @@ pipeline { } } - stage('Build MySQL Service') { - steps { - script { - // Docker로 MySQL 컨테이너 띄우기 (jenkins 서버에 Docker가 설치되어 있어야 함) - sh ''' - docker run -d \ - --name mysql-ci \ - -e MYSQL_DATABASE=${DB_NAME} \ - -e MYSQL_ROOT_PASSWORD=root \ - -e MYSQL_USER=homeaid_user \ - -e MYSQL_PASSWORD=${DB_PASSWORD} \ - -p 3306:3306 \ - --health-cmd="mysqladmin ping -h localhost --silent" \ - --health-interval=10s \ - --health-timeout=5s \ - --health-retries=3 \ - mysql:latest - - # DB 준비 대기 (최대 60초) - for i in {1..12}; do - if docker exec mysql-ci mysqladmin ping -h localhost --silent; then - echo "MySQL is ready!" - break - fi - echo "Waiting for MySQL..." - sleep 5 - done - ''' - } - } - } - stage('Build and Test') { steps { sh 'chmod +x ./gradlew' From d7556f7dcaa13f8ddec2de4b7da7ae072e4fdcbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=83=81=EC=9A=B0=EC=A7=84=20=28Sang=20Woo=20Jin=29?= <80154292+SangWJDev@users.noreply.github.com> Date: Wed, 4 Jun 2025 09:18:34 +0900 Subject: [PATCH 15/20] Update docker-compose.yml --- docker-compose.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index 7b9b2937..db8873c0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -20,6 +20,8 @@ services: environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: homeaid_db + MYSQL_USER: homeaid_user + MYSQL_PASSWORD: root healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] interval: 30s From feee871d02494ed121951b47e2923247095cac6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=83=81=EC=9A=B0=EC=A7=84=20=28Sang=20Woo=20Jin=29?= <80154292+SangWJDev@users.noreply.github.com> Date: Wed, 4 Jun 2025 09:19:04 +0900 Subject: [PATCH 16/20] Update Jenkinsfile --- Jenkinsfile | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 0a1aac3b..7c0aaa74 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -77,11 +77,7 @@ pipeline { } } always { - // Clean up MySQL container - sh ''' - docker stop mysql-ci || true - docker rm mysql-ci || true - ''' + } } } From d3701d43717843a85c011f84409568e24b939545 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=83=81=EC=9A=B0=EC=A7=84=20=28Sang=20Woo=20Jin=29?= <80154292+SangWJDev@users.noreply.github.com> Date: Wed, 4 Jun 2025 09:25:00 +0900 Subject: [PATCH 17/20] Update Jenkinsfile --- Jenkinsfile | 3 --- 1 file changed, 3 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 7c0aaa74..320827d7 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -76,8 +76,5 @@ pipeline { """ } } - always { - - } } } From c325b87e36f0642d58db69d828dd3178010627fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=83=81=EC=9A=B0=EC=A7=84=20=28Sang=20Woo=20Jin=29?= <80154292+SangWJDev@users.noreply.github.com> Date: Wed, 4 Jun 2025 12:07:03 +0900 Subject: [PATCH 18/20] Update Jenkinsfile --- Jenkinsfile | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Jenkinsfile b/Jenkinsfile index 320827d7..3cf2938f 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -22,6 +22,16 @@ pipeline { } } + stage('Set Variables') { + steps { + wrap([$class: 'BuildUser']) { + script { + echo "Triggered by: ${env.BUILD_USER}" + } + } + } + } + stage('Build and Test') { steps { sh 'chmod +x ./gradlew' From d343ad5bb1b982ace230d8589dc0e4581474b7de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=83=81=EC=9A=B0=EC=A7=84=20=28Sang=20Woo=20Jin=29?= <80154292+SangWJDev@users.noreply.github.com> Date: Wed, 4 Jun 2025 12:08:33 +0900 Subject: [PATCH 19/20] Update Jenkinsfile --- Jenkinsfile | 66 +++++++++++++++++++++++++++-------------------------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 3cf2938f..14c11019 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -51,39 +51,41 @@ pipeline { post { success { - script { - def message = """{ - "embeds": [{ - "title": "✅ CI 성공", - "description": "**📦 Repository:** `${env.JOB_NAME}`\\n**🌿 Branch:** `${env.BRANCH_NAME}`\\n**👤 Triggered by:** `${env.BUILD_USER}`\\n[🔗 Jenkins 로그 확인하기](${env.BUILD_URL})", - "color": 5763719 - }], - "content": "✅ CI 통과: `${env.BRANCH_NAME}` 브랜치입니다!" - }""" - sh """ - curl -H "Content-Type: application/json" \ - -X POST \ - -d '${message}' \ - ${DISCORD_WEBHOOK} - """ + wrap([$class: 'BuildUser']) { + script { + def message = """{ + "embeds": [{ + "title": "✅ CI 성공", + "description": "**📦 Repository:** `${env.JOB_NAME}`\\n**🌿 Branch:** `${env.BRANCH_NAME}`\\n**👤 Triggered by:** `${env.BUILD_USER}`\\n[🔗 Jenkins 로그 확인하기](${env.BUILD_URL})", + "color": 5763719 + }], + "content": "✅ CI 통과: `${env.BRANCH_NAME}` 브랜치입니다!" + }""" + sh """ + curl -H "Content-Type: application/json" \ + -X POST \ + -d '${message}' \ + ${DISCORD_WEBHOOK} + """ + } } - } - failure { - script { - def message = """{ - "embeds": [{ - "title": "❌ CI 실패", - "description": "**📦 Repository:** `${env.JOB_NAME}`\\n**🌿 Branch:** `${env.BRANCH_NAME}`\\n**👤 Triggered by:** `${env.BUILD_USER}`\\n[🔗 Jenkins 로그 확인하기](${env.BUILD_URL})", - "color": 16711680 - }], - "content": "❗ CI 실패 발생: `${env.BRANCH_NAME}` 브랜치 확인해주세요!" - }""" - sh """ - curl -H "Content-Type: application/json" \ - -X POST \ - -d '${message}' \ - ${DISCORD_WEBHOOK} - """ + failure { + script { + def message = """{ + "embeds": [{ + "title": "❌ CI 실패", + "description": "**📦 Repository:** `${env.JOB_NAME}`\\n**🌿 Branch:** `${env.BRANCH_NAME}`\\n**👤 Triggered by:** `${env.BUILD_USER}`\\n[🔗 Jenkins 로그 확인하기](${env.BUILD_URL})", + "color": 16711680 + }], + "content": "❗ CI 실패 발생: `${env.BRANCH_NAME}` 브랜치 확인해주세요!" + }""" + sh """ + curl -H "Content-Type: application/json" \ + -X POST \ + -d '${message}' \ + ${DISCORD_WEBHOOK} + """ + } } } } From 8aad10ac323775e4d97205c0160174e5139b2215 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=83=81=EC=9A=B0=EC=A7=84=20=28Sang=20Woo=20Jin=29?= <80154292+SangWJDev@users.noreply.github.com> Date: Wed, 4 Jun 2025 13:36:39 +0900 Subject: [PATCH 20/20] Update Jenkinsfile --- Jenkinsfile | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 14c11019..e58b4092 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -4,7 +4,7 @@ pipeline { environment { DISCORD_WEBHOOK = credentials('discord-webhook') DB_DRIVER = 'mysql' - DB_HOST = 'mysql-ci' // Docker Compose 서비스명 (같은 네트워크) + DB_HOST = 'mysql-ci' DB_PORT = '3306' DB_NAME = 'homeaid_db' DB_USERNAME = 'homeaid_user' @@ -26,6 +26,7 @@ pipeline { steps { wrap([$class: 'BuildUser']) { script { + env.BUILD_USER = "${env.BUILD_USER}" echo "Triggered by: ${env.BUILD_USER}" } } @@ -69,7 +70,9 @@ pipeline { """ } } - failure { + } + failure { + wrap([$class: 'BuildUser']) { script { def message = """{ "embeds": [{