Skip to content

Commit

Permalink
Merge pull request #20 from Billing-Wise/dev
Browse files Browse the repository at this point in the history
[release] 1.0.2
  • Loading branch information
dtd1614 authored Aug 5, 2024
2 parents 3b07643 + f0b5acf commit 9b52c9b
Show file tree
Hide file tree
Showing 38 changed files with 5,843 additions and 432 deletions.
9 changes: 8 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ plugins {
}

group = 'site.billingwise.batch'
version = '1.0.1'
version = '1.0.2'

java {
toolchain {
Expand Down Expand Up @@ -48,6 +48,13 @@ dependencies {

// prometheus
implementation 'io.micrometer:micrometer-registry-prometheus'

// Logback Slack Appender
implementation 'com.github.maricn:logback-slack-appender:1.6.0'

// Logstash Logback Encoder for JSON logs
implementation 'net.logstash.logback:logstash-logback-encoder:7.4'

}

ext {
Expand Down
1,482 changes: 1,482 additions & 0 deletions logs/application-2024-08-02.0.log

Large diffs are not rendered by default.

3,311 changes: 3,311 additions & 0 deletions logs/batch-logs.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,4 @@ public class ServerBatchApplication {
public static void main(String[] args) {
SpringApplication.run(ServerBatchApplication.class, args);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package site.billingwise.batch.server_batch.batch.controller;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.UUID;

@RestController
@RequestMapping("/api/batch")
@RequiredArgsConstructor
@Slf4j
public class BatchJobController {

private final JobLauncher jobLauncher;
private final Job jdbcGenerateInvoiceJob;
private final Job invoiceProcessingJob;
private final Job weeklyInvoiceStatisticsJob;
private final Job monthlyInvoiceStatisticsJob;

@PostMapping("/generate-invoice")
public String runJdbcGenerateInvoiceJob() {
return launchJob(jdbcGenerateInvoiceJob, "jdbcInvoice");
}

@PostMapping("/process-invoice")
public String runInvoiceProcessingJob() {
return launchJob(invoiceProcessingJob, "InvoiceProcessingJob");
}

@PostMapping("/weekly-statistics")
public String runWeeklyStatisticsJob() {
return launchJob(weeklyInvoiceStatisticsJob, "weeklyInvoiceStatisticsJob");
}

@PostMapping("/monthly-statistics")
public String runMonthlyStatisticsJob() {
return launchJob(monthlyInvoiceStatisticsJob, "monthlyInvoiceStatisticsJob");
}



private String launchJob(Job job, String jobName) {
String uuid = UUID.randomUUID().toString();
JobParameters jobParameters = new JobParametersBuilder()
.addLong(jobName, System.currentTimeMillis())
.addString("UUID", uuid)
.toJobParameters();
try {
jobLauncher.run(job, jobParameters);
return "Job " + jobName + " submitted successfully. UUID: " + uuid;
} catch (Exception e) {
log.error("Error occurred while starting job: {} with UUID: {}. Error: {}", jobName, uuid, e.getMessage());
return "Error occurred while starting job: " + jobName + ". UUID: " + uuid;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package site.billingwise.batch.server_batch.batch.generateinvoice;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.item.Chunk;
import org.springframework.batch.item.ItemWriter;
import org.springframework.stereotype.Component;
Expand All @@ -13,9 +14,12 @@
import java.time.LocalDate;
import java.time.LocalDateTime;

import static site.billingwise.batch.server_batch.batch.util.StatusConstants.INVOICE_TYPE_MANUAL_BILLING;


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

private final PaymentStatusRepository paymentStatusRepository;
Expand All @@ -34,17 +38,27 @@ public void write(Chunk<? extends Contract> chunk) throws Exception {
for (Contract contract : chunk) {
boolean exists = invoiceRepository.existsByContractAndMonthAndYear(contract, nextMonthValue, yearValue);

if(INVOICE_TYPE_MANUAL_BILLING == contract.getInvoiceType().getId()) {
continue;
}

if(contract.getIsDeleted()){
continue;
}

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())
.isDeleted(false)
.dueDate(dueDate)
.build();
invoiceRepository.save(invoice);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,26 @@
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.core.step.tasklet.TaskletStep;
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.CustomRetryListener;
import site.billingwise.batch.server_batch.batch.listner.CustomSkipListener;
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.batch.listner.StepCompletionCheckListener;
import site.billingwise.batch.server_batch.batch.policy.backoff.CustomBackOffPolicy;
import site.billingwise.batch.server_batch.batch.policy.skip.CustomSkipPolicy;
import site.billingwise.batch.server_batch.domain.contract.Contract;

import javax.sql.DataSource;


@Configuration
@RequiredArgsConstructor
public class JdbcGenerateInvoiceJobConfig {
Expand All @@ -28,6 +35,10 @@ public class JdbcGenerateInvoiceJobConfig {
private final DataSource dataSource;
private final JdbcTemplate jdbcTemplate;
private final JobCompletionCheckListener jobCompletionCheckListener;
private final CustomRetryListener retryListener;
private final CustomSkipListener customSkipListener;
private final CustomSkipPolicy customSkipPolicy;
private final StepCompletionCheckListener stepCompletionCheckListener;

@Bean
public Job jdbcGenerateInvoiceJob(JobRepository jobRepository, Step jdbcGenerateInvoiceStep) {
Expand All @@ -38,23 +49,42 @@ public Job jdbcGenerateInvoiceJob(JobRepository jobRepository, Step jdbcGenerate
}



@Bean
public Step jdbcGenerateInvoiceStep(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
return new StepBuilder("jdbcGenerateInvoiceStep", jobRepository)

CustomBackOffPolicy customBackOffPolicy = new CustomBackOffPolicy(1000L, 2.0, 4000L);

TaskletStep jdbcGenerateInvoiceStep = new StepBuilder("jdbcGenerateInvoiceStep", jobRepository)
.<Contract, Contract>chunk(CHUNK_SIZE, transactionManager)
.reader(jdbcContractItemReader())
.writer(jdbcContractItemWriter())
.faultTolerant()
.retry(Exception.class)
.retryLimit(2)
.backOffPolicy(customBackOffPolicy)
.listener(retryListener)
.skip(Exception.class)
.skipPolicy(customSkipPolicy)
.listener(customSkipListener)
.listener(stepCompletionCheckListener)
.build();
return jdbcGenerateInvoiceStep;
}

@Bean
public ItemReader<Contract> jdbcContractItemReader() {
String sql = """
select con.contract_id, con.invoice_type_id, con.payment_type_id, con.contract_cycle,
con.item_price, con.item_amount, con.is_deleted, con.is_subscription, con.payment_due_cycle
from contract con
where con.contract_status_id = 2 and con.is_deleted = false
""";

return new JdbcCursorItemReaderBuilder<Contract>()
.name("jdbcContractItemReader")
.fetchSize(CHUNK_SIZE)
.sql("select c.*, c.is_deleted " +
"from contract c " +
"where c.contract_status_id = 2")
.sql(sql)
.rowMapper(new JdbcContractRowMapper())
.dataSource(dataSource)
.build();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package site.billingwise.batch.server_batch.batch.generateinvoice.jdbc;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.item.Chunk;
import org.springframework.batch.item.ItemWriter;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
Expand All @@ -23,14 +24,13 @@

@Component
@RequiredArgsConstructor
@Slf4j
public class JdbcGenerateInvoiceWriter implements ItemWriter<Contract> {

private final JdbcTemplate jdbcTemplate;


@Override
public void write(Chunk<? extends Contract> chunk) throws Exception {

LocalDateTime now = LocalDateTime.now();
LocalDateTime nextMonth = now.plusMonths(1);
int nextMonthValue = nextMonth.getMonthValue();
Expand All @@ -40,23 +40,19 @@ public void write(Chunk<? extends Contract> chunk) throws Exception {

List<Invoice> invoices = new ArrayList<>();

for (Contract contract : chunk) {

for(Contract contract : chunk) {
// 수동 청구면 pass(애초에 계약이 수동 청구인 경우)
if(INVOICE_TYPE_MANUAL_BILLING == contract.getInvoiceType().getId()) {
continue;
}

if(contract.getIsDeleted()){
// 수동 청구 계약은 건너뜀
if (INVOICE_TYPE_MANUAL_BILLING == contract.getInvoiceType().getId()) {
continue;
}


// 청구가 이미 만들어져 있으면, pass( 원래는 자동 청구인데, 단발성으로 청구를 생성한 경우 )
if(!invoiceExists(contract, nextMonthValue, yearValue)){
// 약정일
// 해당 월에 이미 청구서가 존재하는지 확인
if (!invoiceExists(contract, nextMonthValue, yearValue)) {
// 청구일 설정
LocalDateTime setInvoiceDate = LocalDateTime.of(yearValue, nextMonthValue, contract.getContractCycle(), 0, 0);
// 결제기한
// 납부 기한 계산
LocalDateTime payDueDate = calculateDueDate(contract, setInvoiceDate);

Invoice invoice = Invoice.builder()
Expand All @@ -73,7 +69,6 @@ public void write(Chunk<? extends Contract> chunk) throws Exception {
.build();

invoices.add(invoice);

}
}

Expand All @@ -86,8 +81,7 @@ private void bulkInsertInvoices(List<Invoice> invoices) {
String sql = "insert into invoice (contract_id, invoice_type_id, payment_type_id, payment_status_id, charge_amount, contract_date, due_date, is_deleted, created_at, updated_at)" +
" values (?, ?, ?, ?, ?, ?, ?, false, NOW(), NOW())";

jdbcTemplate.batchUpdate(sql,new BatchPreparedStatementSetter() {

jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
Invoice invoice = invoices.get(i);
Expand All @@ -107,6 +101,7 @@ public int getBatchSize() {
});
}

// '대기' 상태의 PaymentStatus 조회 메서드
private PaymentStatus findPendingPaymentStatus() {
String sql = "select payment_status_id, name from payment_status where name = '대기'";
return jdbcTemplate.queryForObject(sql, (ResultSet rs, int rowNum) ->
Expand All @@ -115,15 +110,17 @@ private PaymentStatus findPendingPaymentStatus() {
.build());
}

// 납부 기한 계산 메서드
private LocalDateTime calculateDueDate(Contract contract, LocalDateTime setInvoiceDate) {
if (PAYMENT_TYPE_PAYER_PAYMENT == contract.getPaymentType().getId()) {
return setInvoiceDate.plusDays(3);
return setInvoiceDate.plusDays(contract.getPaymentDueCycle());
}
return setInvoiceDate;
}

// 해당 월에 청구서가 이미 존재하는지 확인
private boolean invoiceExists(Contract contract, int month, int year) {
LocalDateTime startDate = LocalDateTime.of(year, month, 1, 0, 0,0);
LocalDateTime startDate = LocalDateTime.of(year, month, 1, 0, 0, 0);
LocalDateTime endDate = startDate.plusMonths(1).minusSeconds(1);

String sql = "select count(*) from invoice where contract_id = ? " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,56 +2,33 @@

import org.springframework.jdbc.core.RowMapper;
import site.billingwise.batch.server_batch.domain.contract.Contract;
import site.billingwise.batch.server_batch.domain.contract.ContractStatus;
import site.billingwise.batch.server_batch.domain.contract.PaymentType;
import site.billingwise.batch.server_batch.domain.invoice.InvoiceType;
import site.billingwise.batch.server_batch.domain.item.Item;
import site.billingwise.batch.server_batch.domain.member.Member;


import java.sql.ResultSet;
import java.sql.SQLException;

public class JdbcContractRowMapper implements RowMapper<Contract> {
@Override
public Contract mapRow(ResultSet rs, int rowNum) throws SQLException {

Member member = Member.builder()
.id(rs.getLong("member_id"))
.build();


Item item = Item.builder()
.id(rs.getLong("item_id"))
.build();


InvoiceType invoiceType = InvoiceType.builder()
.id(rs.getLong("invoice_type_id"))
.build();


PaymentType paymentType = PaymentType.builder()
.id(rs.getLong("payment_type_id"))
.build();

ContractStatus contractStatus = ContractStatus.builder()
.id(rs.getLong("contract_status_id"))
.build();


return Contract.builder()
.id(rs.getLong("contract_id"))
.member(member)
.item(item)
.invoiceType(invoiceType)
.paymentType(paymentType)
.contractStatus(contractStatus)
.paymentDueCycle(rs.getInt("payment_due_cycle"))
.isSubscription(rs.getBoolean("is_subscription"))
.itemPrice(rs.getLong("item_price"))
.itemAmount(rs.getInt("item_amount"))
.contractCycle(rs.getInt("contract_cycle"))
.paymentDueCycle(rs.getInt("payment_due_cycle"))
.isEasyConsent(rs.getBoolean("is_easy_consent"))
.isDeleted(rs.getBoolean("is_deleted"))
.build();
}
Expand Down
Loading

0 comments on commit 9b52c9b

Please sign in to comment.