Skip to content

Commit

Permalink
Merge pull request #14 from Billing-Wise/dev
Browse files Browse the repository at this point in the history
[release] 배포
  • Loading branch information
dtd1614 authored Jul 22, 2024
2 parents b16d934 + 439386b commit 1417d68
Show file tree
Hide file tree
Showing 63 changed files with 4,299 additions and 4 deletions.
11 changes: 11 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
## 관련 이슈

- [지라 이슈 코드를 적어주세요.]

## 구현 기능

- 구현 기능을 적어주세요.

## 참고 사항

- 참고 사항을 적어주세요.
48 changes: 48 additions & 0 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: CD
on:
push:
branches:
- prod
jobs:
deploy:
name: Deploy to AWS
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: create secure file
run: |
cd src/main/resources
touch secure.properties
echo "${{ secrets.SECURE }}" >> secure.properties
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ap-northeast-2

- name: Login to Amazon ECR
uses: aws-actions/amazon-ecr-login@v1

- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: 17
distribution: 'temurin'
cache: gradle

- name: Grant execute permission to Gradle Wrapper
run: chmod +x ./gradlew

- name: Build and push image to Amazon ECR
run: ./gradlew clean jib -x test

- name: Deploy to AWS ECS
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: task-definition.json
cluster: t5-ecs-fargate-cluster
service: t5-batch-service
wait-for-service-stability: true
45 changes: 45 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: CI
on:
push:
branches:
- prod
- dev
pull_request:

jobs:
sonarcloud:
name: SonarCloud
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0

- name: create secure file
run: |
cd src/main/resources
touch secure.properties
echo "${{ secrets.SECURE }}" >> secure.properties
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: 17
distribution: 'temurin'
cache: 'gradle'

- name: Cache SonarCloud packages
uses: actions/cache@v3
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar

- name: Grant execute permission to Gradle Wrapper
run: chmod +x ./gradlew

- name: Build and analyze
run: ./gradlew build jacocoTestReport sonar --info
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,5 @@ out/

### VS Code ###
.vscode/

secure.properties
49 changes: 49 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ plugins {
id 'java'
id 'org.springframework.boot' version '3.2.7'
id 'io.spring.dependency-management' version '1.1.5'
id 'com.google.cloud.tools.jib' version '3.4.3'
id 'org.sonarqube' version '4.4.1.3373'
id 'jacoco'
}

group = 'site.billingwise.batch'
Expand All @@ -28,15 +31,61 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-mail'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
implementation 'net.nurigo:sdk:4.3.0'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
runtimeOnly 'com.mysql:mysql-connector-j'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.junit.jupiter:junit-jupiter-engine'
testImplementation 'org.mockito:mockito-junit-jupiter'
testImplementation 'org.springframework.batch:spring-batch-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

ext {
set('springCloudVersion', "2023.0.2")
}

dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}


tasks.named('test') {
useJUnitPlatform()
}

jacocoTestReport {
reports {
xml.required.set(true)
}
}

jib {
from {
image = "eclipse-temurin:17-jdk"
}
to {
image = "891376922202.dkr.ecr.ap-northeast-2.amazonaws.com/t5-batch-ecr"
tags = ["${project.version}".toString()]
credHelper = 'ecr-login'
}
container {
creationTime = "USE_CURRENT_TIMESTAMP"
jvmFlags = ['-Dspring.profiles.active=prod', '-XX:+UseContainerSupport', '-Dserver.port=9090', '-Dfile.encoding=UTF-8', '-Duser.timezone=Asia/Seoul']
ports = ['9090']
}
}

sonar {
properties {
property "sonar.projectKey", "Billing-Wise_sever-batch"
property "sonar.organization", "billing-wise"
property "sonar.host.url", "https://sonarcloud.io"
property 'sonar.coverage.jacoco.xmlReportPaths', 'build/reports/jacoco/test/jacocoTestReport.xml'
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,14 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.PropertySource;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@SpringBootApplication
@EnableJpaAuditing
@EnableFeignClients
@PropertySource("classpath:secure.properties")
public class ServerBatchApplication {

public static void main(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package site.billingwise.batch.server_batch.batch.generateinvoice;


import jakarta.persistence.EntityManagerFactory;
import lombok.RequiredArgsConstructor;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.database.JpaPagingItemReader;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import site.billingwise.batch.server_batch.batch.listner.JobCompletionCheckListener;
import site.billingwise.batch.server_batch.domain.contract.Contract;
import site.billingwise.batch.server_batch.domain.invoice.repository.InvoiceRepository;
import site.billingwise.batch.server_batch.domain.invoice.repository.PaymentStatusRepository;

@Configuration
@RequiredArgsConstructor
public class GenerateInvoiceJobConfig {

private final int CHUNK_SIZE = 100;
private final EntityManagerFactory entityManagerFactory;
private final JobCompletionCheckListener jobCompletionCheckListener;

@Bean
public Job generateInvoiceJob(JobRepository jobRepository, Step generateInvoiceStep) {
return new JobBuilder("generateInvoiceJob", jobRepository)
.listener(jobCompletionCheckListener)
.start(generateInvoiceStep)
.build();
}

@Bean
public Step generateInvoiceStep(JobRepository jobRepository, PlatformTransactionManager transactionManager,
JpaPagingItemReader<Contract> contractItemReader, ItemWriter<Contract> contractItemWriter) {
return new StepBuilder("generateInvoiceStep", jobRepository)
.<Contract, Contract>chunk(CHUNK_SIZE, transactionManager)
.reader(contractItemReader)
.writer(contractItemWriter)
.build();
}

@Bean
public JpaPagingItemReader<Contract> contractItemReader() {
JpaPagingItemReader<Contract> reader = new JpaPagingItemReader<>();
reader.setEntityManagerFactory(entityManagerFactory);
reader.setQueryString("SELECT c FROM Contract c WHERE c.contractStatus.name = '진행' ORDER BY c.id ASC");
reader.setPageSize(CHUNK_SIZE);
return reader;
}

@Bean
public ItemWriter<Contract> contractItemWriter(PaymentStatusRepository paymentStatusRepository, InvoiceRepository invoiceRepository) {
return new GenerateInvoiceWriter(paymentStatusRepository, invoiceRepository);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package site.billingwise.batch.server_batch.batch.generateinvoice;

import lombok.RequiredArgsConstructor;
import org.springframework.batch.item.Chunk;
import org.springframework.batch.item.ItemWriter;
import org.springframework.stereotype.Component;
import site.billingwise.batch.server_batch.domain.contract.Contract;
import site.billingwise.batch.server_batch.domain.invoice.Invoice;
import site.billingwise.batch.server_batch.domain.invoice.PaymentStatus;
import site.billingwise.batch.server_batch.domain.invoice.repository.InvoiceRepository;
import site.billingwise.batch.server_batch.domain.invoice.repository.PaymentStatusRepository;

import java.time.LocalDate;
import java.time.LocalDateTime;


@Component
@RequiredArgsConstructor
public class GenerateInvoiceWriter implements ItemWriter<Contract> {

private final PaymentStatusRepository paymentStatusRepository;
private final InvoiceRepository invoiceRepository;

@Override
public void write(Chunk<? extends Contract> chunk) throws Exception {
LocalDate now = LocalDate.now();
LocalDate nextMonth = now.plusMonths(1);
int nextMonthValue = nextMonth.getMonthValue();
int yearValue = nextMonth.getYear();

PaymentStatus paymentStatusUnpaid = paymentStatusRepository.findByName("미납")
.orElseThrow(() -> new IllegalArgumentException("결제 미납 상태가 존재하지 않습니다."));

for (Contract contract : chunk) {
boolean exists = invoiceRepository.existsByContractAndMonthAndYear(contract, nextMonthValue, yearValue);

if (!exists) {
LocalDate setContractDate = LocalDate.of(yearValue, nextMonthValue, contract.getContractCycle());
LocalDateTime dueDate = calculateDueDate(contract, setContractDate);

Invoice invoice = Invoice.builder()
.contract(contract)
.invoiceType(contract.getInvoiceType())
.paymentType(contract.getPaymentType())
.paymentStatus(paymentStatusUnpaid)
.chargeAmount(contract.getItemPrice() * contract.getItemAmount())
.contractDate(setContractDate.atStartOfDay())
.dueDate(dueDate)
.build();
invoiceRepository.save(invoice);
}
}
}

private LocalDateTime calculateDueDate(Contract contract, LocalDate setContractDate) {
if (contract.getPaymentType().getName().equals("납부자 결제")) {
return setContractDate.plusDays(contract.getPaymentDueCycle()).atStartOfDay();
}
return setContractDate.atStartOfDay();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package site.billingwise.batch.server_batch.batch.generateinvoice.jdbc;


import lombok.RequiredArgsConstructor;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.database.builder.JdbcCursorItemReaderBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.PlatformTransactionManager;
import site.billingwise.batch.server_batch.batch.listner.JobCompletionCheckListener;
import site.billingwise.batch.server_batch.batch.generateinvoice.rowmapper.JdbcContractRowMapper;
import site.billingwise.batch.server_batch.domain.contract.Contract;

import javax.sql.DataSource;

@Configuration
@RequiredArgsConstructor
public class JdbcGenerateInvoiceJobConfig {

private final int CHUNK_SIZE = 100;
private final DataSource dataSource;
private final JdbcTemplate jdbcTemplate;
private final JobCompletionCheckListener jobCompletionCheckListener;

@Bean
public Job jdbcGenerateInvoiceJob(JobRepository jobRepository, Step jdbcGenerateInvoiceStep) {
return new JobBuilder("jdbcGenerateInvoiceJob", jobRepository)
.listener(jobCompletionCheckListener)
.start(jdbcGenerateInvoiceStep)
.build();
}


@Bean
public Step jdbcGenerateInvoiceStep(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
return new StepBuilder("jdbcGenerateInvoiceStep", jobRepository)
.<Contract, Contract>chunk(CHUNK_SIZE, transactionManager)
.reader(jdbcContractItemReader())
.writer(jdbcContractItemWriter())
.build();
}

@Bean
public ItemReader<Contract> jdbcContractItemReader() {
return new JdbcCursorItemReaderBuilder<Contract>()
.name("jdbcContractItemReader")
.fetchSize(CHUNK_SIZE)
.sql("select c.*, c.is_deleted " +
"from contract c " +
"where c.contract_status_id = 2")
.rowMapper(new JdbcContractRowMapper())
.dataSource(dataSource)
.build();
}




@Bean
public ItemWriter<Contract> jdbcContractItemWriter() {
return new JdbcGenerateInvoiceWriter(jdbcTemplate);
}
}
Loading

0 comments on commit 1417d68

Please sign in to comment.