Skip to content

Commit

Permalink
Merge pull request #303 from JNU-econovation/be
Browse files Browse the repository at this point in the history
[BE] merge to main
  • Loading branch information
kssumin authored Mar 3, 2024
2 parents 2a4a903 + 2464338 commit 77ffb0b
Show file tree
Hide file tree
Showing 13 changed files with 177 additions and 10 deletions.
28 changes: 28 additions & 0 deletions BE/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
## TechStack

| 카테고리 | 기술 |
| --- | --- |
| Language, Framework | Java, Spring Boot |
| Database | MySQL, Redis, Flyway |
| ORM | Spring Data JPA, QueryDSL |
| 운영 환경 구축 | ECR, ECS, EC2, Docker |
| Build | Gradle |
| CI/CD | Github Actions |
| library | Spring Open Feign, OAuth 2.0 |

## docs
[서브 도메인 적용하기](https://velog.io/@kssumin/%EC%84%9C%EB%B8%8C-%EB%8F%84%EB%A9%94%EC%9D%B8-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0)

[타임존 설정](https://velog.io/@kssumin/DB%EC%97%90-%EB%93%A4%EC%96%B4%EA%B0%80%EB%8A%94-%EC%8B%9C%EA%B0%84%EC%9D%B4-%EC%9D%B4%EC%83%81%ED%95%98%EB%8B%A4)

[분기 처리 코드 리팩터링하기](https://velog.io/@kssumin/%EB%B6%84%EA%B8%B0%EC%B2%98%EB%A6%AC-%EC%BD%94%EB%93%9C-%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81-%ED%95%98%EA%B8%B0)

[슬랙 API 호출 코드를 추상화하기](https://velog.io/@kssumin/%EC%8A%AC%EB%9E%99-API-%ED%98%B8%EC%B6%9C-%EC%BD%94%EB%93%9C%EB%A5%BC-%EC%B6%94%EC%83%81%ED%99%94%ED%95%98%EA%B8%B0)

[200 status code만 사용하지 말자](https://velog.io/@kssumin/EEOS-%EC%BA%90%EC%8B%B1)

## 실행방법
1. git clone
2. 환경변수를 설정해줍니다.(env.properties)
3. docker-compose를 통해 db를 실행합니다.
4. spring boot를 실행합니다.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.blackcompany.eeos.config;

import java.util.concurrent.TimeUnit;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.CacheControl;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.mvc.WebContentInterceptor;

@Configuration
public class CacheWebConfig implements WebMvcConfigurer {

@Override
public void addInterceptors(final InterceptorRegistry registry) {
CacheControl cacheControl = CacheControl.maxAge(60, TimeUnit.SECONDS);

WebContentInterceptor interceptor = new WebContentInterceptor();
interceptor.addCacheMapping(cacheControl, "/api/**");

registry.addInterceptor(interceptor);
}
}
18 changes: 18 additions & 0 deletions BE/eeos/src/main/java/com/blackcompany/eeos/config/EtagConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.blackcompany.eeos.config;

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.ShallowEtagHeaderFilter;

@Configuration
public class EtagConfig {
@Bean
public FilterRegistrationBean<ShallowEtagHeaderFilter> shallowEtagHeaderFilter() {
FilterRegistrationBean<ShallowEtagHeaderFilter> filterRegistrationBean =
new FilterRegistrationBean<>(new ShallowEtagHeaderFilter());
filterRegistrationBean.addUrlPatterns("/api/*");

return filterRegistrationBean;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
Expand All @@ -17,6 +18,7 @@ public class QueryTeamBuildingTargetService {
private final TeamBuildingTargetRepository teamBuildingTargetRepository;
private final TeamBuildingTargetEntityConverter entityConverter;

@Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
public TeamBuildingTargetModel getTarget(Long memberId, Long teamBuildingId) {
return teamBuildingTargetRepository
.findByTeamBuildingIdAndMemberId(teamBuildingId, memberId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ Optional<TeamBuildingTargetEntity> findByTeamBuildingIdAndMemberId(
Long teamBuildingId, Long memberId);

List<TeamBuildingTargetEntity> findByTeamBuildingId(Long teamBuildingId);

void deleteByTeamBuildingId(Long teamBuildingId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.blackcompany.eeos.teamBuilding.application.event;

import lombok.Getter;

@Getter
public class DeletedTeamBuildingEvent {
private final Long teamBuildingId;

private DeletedTeamBuildingEvent(Long teamBuildingId) {
this.teamBuildingId = teamBuildingId;
}

/**
* 팀빌딩 삭제 이벤트를 생성한다.
*
* <p>이벤트 생성에서 예외가 발생하면 서비스 로직에 영향을 주므로 이벤트 리스너에서 핸들링을 할 때 예외 처리를 한다.
*
* @param teamBuildingId 삭제된 팀빌딩 정보
* @return
*/
public static DeletedTeamBuildingEvent of(Long teamBuildingId) {
return new DeletedTeamBuildingEvent(teamBuildingId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.blackcompany.eeos.teamBuilding.application.event;

import com.blackcompany.eeos.target.persistence.TeamBuildingTargetRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;
import org.springframework.transaction.support.TransactionSynchronizationManager;

@Component
@RequiredArgsConstructor
@Slf4j
public class DeletedTeamBuildingEventListener {
private final TeamBuildingTargetRepository teamBuildingTargetRepository;

@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void handleDeletedTeamBuilding(DeletedTeamBuildingEvent event) {
log.info(
"팀빌딩 삭제 Transaction committed: {}",
TransactionSynchronizationManager.isActualTransactionActive());
teamBuildingTargetRepository.deleteByTeamBuildingId(event.getTeamBuildingId());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.blackcompany.eeos.teamBuilding.application.dto.EachMemberResponse;
import com.blackcompany.eeos.teamBuilding.application.dto.ResultTeamBuildingResponse;
import com.blackcompany.eeos.teamBuilding.application.dto.converter.ResultTeamBuildingResponseConverter;
import com.blackcompany.eeos.teamBuilding.application.event.DeletedTeamBuildingEvent;
import com.blackcompany.eeos.teamBuilding.application.exception.CompleteTeamBuildingException;
import com.blackcompany.eeos.teamBuilding.application.exception.EndTeamBuildingException;
import com.blackcompany.eeos.teamBuilding.application.exception.NotFoundProgressTeamBuildingException;
Expand All @@ -20,6 +21,7 @@
import com.blackcompany.eeos.teamBuilding.application.model.converter.TeamBuildingResultConverter;
import com.blackcompany.eeos.teamBuilding.application.usecase.CompleteTeamBuildingUsecase;
import com.blackcompany.eeos.teamBuilding.application.usecase.CreateTeamBuildingUsecase;
import com.blackcompany.eeos.teamBuilding.application.usecase.DeleteTeamBuildingUsecase;
import com.blackcompany.eeos.teamBuilding.application.usecase.EndTeamBuildingUsecase;
import com.blackcompany.eeos.teamBuilding.application.usecase.GetResultTeamBuildingUsecase;
import com.blackcompany.eeos.teamBuilding.application.usecase.GetTeamBuildingUsecase;
Expand All @@ -35,6 +37,7 @@
import java.util.function.Supplier;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -46,7 +49,8 @@ public class TeamBuildingService
EndTeamBuildingUsecase,
CompleteTeamBuildingUsecase,
GetResultTeamBuildingUsecase,
GetTeamBuildingUsecase {
GetTeamBuildingUsecase,
DeleteTeamBuildingUsecase {
private final RestrictTeamBuildingService restrictTeamBuildingService;
private final TeamBuildingRequestConverter requestConverter;
private final TeamBuildingEntityConverter entityConverter;
Expand All @@ -60,6 +64,7 @@ public class TeamBuildingService
private final TeamBuildingResultConverter teamBuildingResultConverter;
private final MemberRepository memberRepository;
private final ResultTeamBuildingResponseConverter responseConverter;
private final ApplicationEventPublisher publisher;

@Override
@Transactional
Expand All @@ -74,7 +79,7 @@ public void create(Long memberId, CreateTeamBuildingRequest request) {

@Override
@Transactional
public void delete(Long memberId) {
public void end(Long memberId) {
TeamBuildingStatus status = TeamBuildingStatus.COMPLETE;

TeamBuildingModel model =
Expand Down Expand Up @@ -135,6 +140,24 @@ public QueryTeamBuildingResponse getTeamBuilding(Long memberId) {
.build();
}

@Override
@Transactional
public void delete(Long memberId) {
TeamBuildingStatus status = TeamBuildingStatus.PROGRESS;

TeamBuildingModel model =
teamBuildingRepository
.findByStatus(status)
.map(entityConverter::from)
.orElseThrow(() -> new NotFoundTeamBuildingStatusException(status.getStatus()));

model.validateEdit(memberId);

teamBuildingRepository.delete(entityConverter.toEntity(model));
restrictTeamBuildingService.subtractTeamBuilding();
publisher.publishEvent(DeletedTeamBuildingEvent.of(model.getId()));
}

private List<List<EachMemberResponse>> combines(
List<List<MemberEntity>> members, List<List<Long>> memberIds) {
return members.stream()
Expand Down Expand Up @@ -191,8 +214,7 @@ private void validateReadAccessibility(Long memberId, Long programId) {
generateNotFoundProgressTeamBuildingException() {
if (teamBuildingRepository.existsByStatus(TeamBuildingStatus.COMPLETE)) {
return CompleteTeamBuildingException::new;
} else {
return EndTeamBuildingException::new;
}
return EndTeamBuildingException::new;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.blackcompany.eeos.teamBuilding.application.usecase;

public interface DeleteTeamBuildingUsecase {
/**
* 팀빌딩 완료를 요청한다.
*
* @param memberId 팀빌딩 작성자
*/
void delete(Long memberId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ public interface EndTeamBuildingUsecase {
*
* @param memberId 팀빌딩 작성자
*/
void delete(Long memberId);
void end(Long memberId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.blackcompany.eeos.teamBuilding.application.dto.ValidateTeamBuildingResponse;
import com.blackcompany.eeos.teamBuilding.application.usecase.CompleteTeamBuildingUsecase;
import com.blackcompany.eeos.teamBuilding.application.usecase.CreateTeamBuildingUsecase;
import com.blackcompany.eeos.teamBuilding.application.usecase.DeleteTeamBuildingUsecase;
import com.blackcompany.eeos.teamBuilding.application.usecase.EndTeamBuildingUsecase;
import com.blackcompany.eeos.teamBuilding.application.usecase.GetResultTeamBuildingUsecase;
import com.blackcompany.eeos.teamBuilding.application.usecase.GetTeamBuildingUsecase;
Expand All @@ -36,6 +37,7 @@ public class TeamBuildingController {
private final CompleteTeamBuildingUsecase completeTeamBuildingUsecase;
private final GetResultTeamBuildingUsecase getResultTeamBuildingUsecase;
private final GetTeamBuildingUsecase getTeamBuildingUsecase;
private final DeleteTeamBuildingUsecase deleteTeamBuildingUsecase;

@PostMapping
public ApiResponse<SuccessBody<Void>> create(
Expand All @@ -46,7 +48,7 @@ public ApiResponse<SuccessBody<Void>> create(

@DeleteMapping("/end")
public ApiResponse<SuccessBody<Void>> end(@Member Long memberId) {
endTeamBuildingUsecase.delete(memberId);
endTeamBuildingUsecase.end(memberId);
return ApiResponseGenerator.success(HttpStatus.OK, MessageCode.DELETE);
}

Expand Down Expand Up @@ -75,4 +77,10 @@ public ApiResponse<SuccessBody<QueryTeamBuildingResponse>> getTeamBuilding(
QueryTeamBuildingResponse response = getTeamBuildingUsecase.getTeamBuilding(memberId);
return ApiResponseGenerator.success(response, HttpStatus.OK, MessageCode.GET);
}

@DeleteMapping
public ApiResponse<SuccessBody<Void>> deleteTeamBuilding(@Member Long memberId) {
deleteTeamBuildingUsecase.delete(memberId);
return ApiResponseGenerator.success(HttpStatus.OK, MessageCode.DELETE);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ void save_team_building() {
@DisplayName("진행 중인 팀빌딩의 작성자가 아니라면 수정 권한이 없음 예외가 발생한다.")
void denied_edit_team_building() {
// given
TeamBuildingEntity 생성된_팀빌딩 = TeamBuildingFixture.팀빌딩(TeamBuildingStatus.COMPLETE, 1L);
when(teamBuildingRepository.findByStatus(TeamBuildingStatus.COMPLETE))
TeamBuildingEntity 생성된_팀빌딩 = TeamBuildingFixture.팀빌딩(TeamBuildingStatus.PROGRESS, 1L);
when(teamBuildingRepository.findByStatus(TeamBuildingStatus.PROGRESS))
.thenReturn(Optional.of(생성된_팀빌딩));

// when & then
Expand Down
6 changes: 4 additions & 2 deletions BE/eeos/tasks/install-git-hooks.gradle
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
tasks.create(name: 'gitExecutableHooks') {
Runtime.getRuntime().exec("chmod -R +x ../../.git/hooks/");
doLast {
Runtime.getRuntime().exec("chmod -R +x ../../.git/hooks/")
}
}

task installGitHooks(type: Copy) {
String scriptDir = rootProject.rootDir.toString() + '/scripts'
from new File(scriptDir, 'pre-commit')
into { new File(rootProject.rootDir, '../../.git/hooks') }
into { new File(rootProject.rootDir, '.git/hooks') }
}

gitExecutableHooks.dependsOn installGitHooks
Expand Down

0 comments on commit 77ffb0b

Please sign in to comment.