Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
15352e9
chore: 프로젝트 설정
nyh365 Jan 27, 2025
e9a9162
chore: 프로젝트 설정
nyh365 Jan 27, 2025
4f37766
feat: baseEntity 추가
nyh365 Jan 27, 2025
98b35b0
feat: 예외처리 추가
nyh365 Jan 27, 2025
940eaec
feat: user 엔티티 추가
nyh365 Jan 27, 2025
31752c4
feat: account 엔티티 추가
nyh365 Jan 27, 2025
8b168a8
feat: 회원 등록 기능 추가
nyh365 Jan 27, 2025
0bb7b18
feat: 이메일로 회원 조회 기능 추가
nyh365 Jan 27, 2025
b39c88e
feat: 메인 계좌 생성 기능 추가
nyh365 Jan 27, 2025
ae4b0bf
feat: 적금 계좌 생성 기능 추가
nyh365 Jan 27, 2025
d8797fc
feat: 예외처리 추가
nyh365 Jan 27, 2025
0478f01
feat: 충전 한도 필드 추가
nyh365 Jan 27, 2025
245990d
feat: 이메일 존재 여부 함수 코드 단으로 변경
nyh365 Jan 27, 2025
37a4adc
feat: 메인 계좌 필드 추가
nyh365 Jan 27, 2025
1a6e47b
feat: 메인 계좌 충전 기능 추가
nyh365 Jan 27, 2025
def1910
feat: 인출 요청 및 응답 dto 추가
nyh365 Jan 28, 2025
bb9f31c
feat: 메인 계좌에서 적금 계좌로 인출하는 기능 추가
nyh365 Jan 28, 2025
49edc05
feat: 충전 한도 타입 수정
nyh365 Feb 2, 2025
9029292
feat: 충전 한도 타입 수정
nyh365 Feb 2, 2025
a39e1d5
chore: 프로젝트 설정
nyh365 Feb 2, 2025
53560c5
feat: 메세지큐 설정 추가
nyh365 Feb 2, 2025
cca982f
refactor: 충전 함수로 리팩토링
nyh365 Feb 2, 2025
6a14bdc
feat: 메인계좌 존재 여부 확인 기능 추가
nyh365 Feb 2, 2025
b0e4b4d
feat: 송금 기능 추가
nyh365 Feb 2, 2025
be1fb48
feat: 이벤트 발행 기능 추가
nyh365 Feb 3, 2025
a980471
feat: 비동기 실행을 위한 설정 추가
nyh365 Feb 3, 2025
4987099
feat: 송금 트랜잭션 엔티티 추가
nyh365 Feb 3, 2025
87eb9c6
feat: 송금 기능 추가
nyh365 Feb 3, 2025
18de610
feat: 송금 기능 추가
nyh365 Feb 3, 2025
0b47496
feat: 누락된 송금 내역 처리를 위한 스케줄러 추가
nyh365 Feb 3, 2025
2d85cde
feat: 충전 한도 0시 00분을 기점으로 초기화하는 스케줄러 추가
nyh365 Feb 3, 2025
7915c2c
refactor: 미사용 코드 제거
nyh365 Feb 3, 2025
aeed0bb
refactor: setter 제거
nyh365 Feb 3, 2025
965c0b2
refactor: 유효성 검증 코드 제거
nyh365 Feb 3, 2025
7aee74a
style: 주석 추가
nyh365 Feb 3, 2025
a0229d2
refactor: 변수명 변경 및 함수화
nyh365 Feb 3, 2025
7dcf5b4
refactor: 함수 위치 변경
nyh365 Feb 3, 2025
01e9522
refactor: 미사용 코드 제거
nyh365 Feb 3, 2025
c704a69
refactor: 미사용 코드 제거
nyh365 Feb 3, 2025
8e88df9
feat: 일일 한도 변경 쿼리 변경
nyh365 Feb 4, 2025
53eaf2c
chore: 메세지 큐 사용을 위한 설정 추가
nyh365 Feb 7, 2025
c4e4638
fix: 적금 계좌 조회 시 락 걸도록 수정
nyh365 Feb 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,14 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.apache.commons:commons-lang3:3.13.0'
implementation 'org.apache.commons:commons-collections4:4.4'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-validation'
runtimeOnly 'com.mysql:mysql-connector-j'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
implementation 'org.springframework.boot:spring-boot-starter-amqp'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.amqp:spring-rabbit-test'
}

checkstyle {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;

@EnableJpaAuditing
@EnableScheduling
@SpringBootApplication
public class AssignmentApplication {

Expand Down
36 changes: 36 additions & 0 deletions src/main/java/org/c4marathon/assignment/config/AsyncConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.c4marathon.assignment.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

@Configuration
@EnableAsync
public class AsyncConfig {
public static final String ASYNC_LISTENER_TASK_EXECUTOR_NAME = "AsyncListenerTaskExecutor";
public static final String ASYNC_SCHEDULER_TASK_EXECUTOR_NAME = "AsyncSchedulerTaskExecutor";
private static final int CORE_POOL_SIZE = 2;
private static final int MAX_POOL_SIZE = 4;

@Bean(name = ASYNC_LISTENER_TASK_EXECUTOR_NAME)
public ThreadPoolTaskExecutor asyncListenerTaskExecutor() {
return getThreadPoolTaskExecutor(ASYNC_LISTENER_TASK_EXECUTOR_NAME);
}

@Bean(name = ASYNC_SCHEDULER_TASK_EXECUTOR_NAME)
public ThreadPoolTaskExecutor asyncSchedulerTaskExecutor() {
return getThreadPoolTaskExecutor(ASYNC_SCHEDULER_TASK_EXECUTOR_NAME);
}

private ThreadPoolTaskExecutor getThreadPoolTaskExecutor(String asyncSchedulerTaskExecutorName) {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(CORE_POOL_SIZE);
executor.setMaxPoolSize(MAX_POOL_SIZE);
executor.setThreadNamePrefix(asyncSchedulerTaskExecutorName);
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(10);
executor.initialize();
return executor;
}
}
76 changes: 76 additions & 0 deletions src/main/java/org/c4marathon/assignment/config/RabbitmqConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package org.c4marathon.assignment.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitmqConfig {

@Value("${spring.rabbitmq.host}")
private String host;

@Value("${spring.rabbitmq.username}")
private String username;

@Value("${spring.rabbitmq.password}")
private String password;

@Value("${spring.rabbitmq.port}")
private int port;

@Value("${rabbitmq.queue.name}")
private String queueName;

@Value("${rabbitmq.exchange.name}")
private String exchangeName;

@Value("${rabbitmq.routing.key}")
private String routingKey;

@Bean
DirectExchange directExchange() {
return new DirectExchange(exchangeName);
}

@Bean
Queue queue() {
return new Queue(queueName);
}

@Bean
Binding binding(DirectExchange directExchange, Queue queue) {
return BindingBuilder.bind(queue).to(directExchange).with(routingKey);
}

@Bean
ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setHost(host);
connectionFactory.setPort(port);
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
return connectionFactory;
}

@Bean
MessageConverter messageConverter() {
return new Jackson2JsonMessageConverter();
}

@Bean
RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory, MessageConverter messageConverter) {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(messageConverter);
return rabbitTemplate;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.c4marathon.assignment.controller;

import org.c4marathon.assignment.dto.request.PostMainAccountReq;
import org.c4marathon.assignment.dto.request.PostSavingsAccountReq;
import org.c4marathon.assignment.dto.request.TransferReq;
import org.c4marathon.assignment.dto.request.WithdrawMainAccountReq;
import org.c4marathon.assignment.dto.response.MainAccountInfoRes;
import org.c4marathon.assignment.dto.response.TransferRes;
import org.c4marathon.assignment.dto.response.WithdrawInfoRes;
import org.c4marathon.assignment.service.AccountService;
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;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;

@RestController
@RequestMapping("/v1/accounts")
@RequiredArgsConstructor
public class AccountController {
private final AccountService accountService;

@PostMapping("/savings")
public void createSavingsAccount(@RequestBody @Valid PostSavingsAccountReq postSavingsAccountReq) {
accountService.createSavingsAccount(postSavingsAccountReq);
}
@PostMapping("/main/deposit")
public MainAccountInfoRes depositMainAccount(@RequestBody @Valid PostMainAccountReq postMainAccountReq) {
return accountService.depositMainAccount(postMainAccountReq);
}
@PostMapping("/savings/withdraw")
public WithdrawInfoRes withdrawForSavings(@RequestBody @Valid WithdrawMainAccountReq withdrawMainAccountReq) {
return accountService.withdrawForSavings(withdrawMainAccountReq);
}

@PostMapping("/main/transfer")
public TransferRes transfer(@RequestBody @Valid TransferReq transferReq) {
return accountService.transfer(transferReq);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.c4marathon.assignment.controller;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.c4marathon.assignment.dto.request.PostUserReq;
import org.c4marathon.assignment.service.UserService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
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("/v1/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;

@PostMapping()
public ResponseEntity<Void> registerUser(@RequestBody @Valid PostUserReq postUserReq) {
userService.registerUser(postUserReq);
return ResponseEntity.status(HttpStatus.CREATED).build();
}
}
27 changes: 27 additions & 0 deletions src/main/java/org/c4marathon/assignment/dto/MessageDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.c4marathon.assignment.dto;

import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Getter
@ToString
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class MessageDto {
long transferTransactionId;

long senderMainAccount;

long receiverMainAccount;
long amount;

@Builder
public MessageDto(long transferTransactionId, long senderMainAccount, long receiverMainAccount, long amount) {
this.transferTransactionId = transferTransactionId;
this.senderMainAccount = senderMainAccount;
this.receiverMainAccount = receiverMainAccount;
this.amount = amount;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.c4marathon.assignment.dto;

import lombok.Builder;
import lombok.Getter;

@Getter
public class TransferTransactionEvent {
private static final int INIT_TRANSFER_TRANSACTION_ID = -1;
String userName;
long transferTransactionId;
long senderMainAccount;
long receiverMainAccount;
long amount;

@Builder
public TransferTransactionEvent(String userName, long senderMainAccount, long receiverMainAccount, long amount) {
this.userName = userName;
this.transferTransactionId = INIT_TRANSFER_TRANSACTION_ID;
this.senderMainAccount = senderMainAccount;
this.receiverMainAccount = receiverMainAccount;
this.amount = amount;
}

public void updateTransferTransactionId(long transferTransactionId) {
this.transferTransactionId = transferTransactionId;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.c4marathon.assignment.dto.request;

import jakarta.validation.constraints.Positive;

public record PostMainAccountReq(
@Positive(message = "회원 번호는 양수가 되어야 합니다.")
long userId,

@Positive(message = "이체 금액은 양수가 되어야 합니다.")
long amount
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.c4marathon.assignment.dto.request;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;

public record PostSavingsAccountReq(
@Email
@NotBlank
String email
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.c4marathon.assignment.dto.request;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;

public record PostUserReq(
@Size(max = 100)
@NotBlank
String username,

@Email
@NotBlank
String email,

@Size(max = 100)
@NotBlank
String nickname
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.c4marathon.assignment.dto.request;

import jakarta.validation.constraints.Positive;

public record TransferReq(
@Positive(message = "사용자 번호는 양수가 되어야 합니다.")
long senderId,

@Positive(message = "메인 계좌번호는 양수가 되어야 합니다.")
long receiverMainAccount,

@Positive(message = "송금 금액은 양수가 되어야 합니다.")
long amount
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.c4marathon.assignment.dto.request;

import jakarta.validation.constraints.Positive;

public record WithdrawMainAccountReq(
@Positive(message = "회원 번호는 양수가 되어야 합니다.")
long userId,
@Positive(message = "적금 계좌번호는 양수가 되어야 합니다.")
long savingsAccount,
@Positive(message = " 금액은 양수가 되어야 합니다.")
long amount
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.c4marathon.assignment.dto.response;

import java.time.Instant;

import org.c4marathon.assignment.entity.Account;

public record MainAccountInfoRes(
Instant depositDate,
Long balance
) {
public MainAccountInfoRes(Account account) {
this(
account.getUpdatedDate(),
account.getBalance()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.c4marathon.assignment.dto.response;

import java.time.Instant;

public record TransferRes(
Instant transferDate,
Long mainAccountBalance
) {
public TransferRes(long mainAccountBalance) {
this(
Instant.now(),
mainAccountBalance
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.c4marathon.assignment.dto.response;

import java.time.Instant;

public record WithdrawInfoRes(
Instant withdrawDate,
Long mainAccountBalance,
Long savingsAccountBalance
) {
public WithdrawInfoRes(long mainAccountBalance, long savingsAccountBalance) {
this(
Instant.now(),
mainAccountBalance,
savingsAccountBalance
);
}
}
Loading
Loading