동기화
This commit is contained in:
@@ -38,16 +38,20 @@ public class BatchConfig {
|
||||
CalcMaintainStep step5,
|
||||
ChargebackExceptionStep step6,
|
||||
CalcOverrideStep step7,
|
||||
AggregateStep step8) {
|
||||
AggregateStep step8,
|
||||
AccountingEntryGenerateStep step9,
|
||||
YearEndIncomeAggregateStep step10) {
|
||||
return new JobBuilder("MONTHLY_SETTLEMENT", jobRepository)
|
||||
.start(taskletStep("receiveData", step1, jobRepository, tx))
|
||||
.next(taskletStep("transformData", step2, jobRepository, tx))
|
||||
.next(taskletStep("reconcile", step3, jobRepository, tx))
|
||||
.next(taskletStep("calcRecruit", step4, jobRepository, tx))
|
||||
.next(taskletStep("calcMaintain", step5, jobRepository, tx))
|
||||
.next(taskletStep("chargebackException", step6, jobRepository, tx))
|
||||
.next(taskletStep("calcOverride", step7, jobRepository, tx))
|
||||
.next(taskletStep("aggregate", step8, jobRepository, tx))
|
||||
.start(taskletStep("receiveData", step1, jobRepository, tx))
|
||||
.next(taskletStep("transformData", step2, jobRepository, tx))
|
||||
.next(taskletStep("reconcile", step3, jobRepository, tx))
|
||||
.next(taskletStep("calcRecruit", step4, jobRepository, tx))
|
||||
.next(taskletStep("calcMaintain", step5, jobRepository, tx))
|
||||
.next(taskletStep("chargebackException", step6, jobRepository, tx))
|
||||
.next(taskletStep("calcOverride", step7, jobRepository, tx))
|
||||
.next(taskletStep("aggregate", step8, jobRepository, tx))
|
||||
.next(taskletStep("accountingEntryGenerate", step9, jobRepository, tx))
|
||||
.next(taskletStep("yearEndIncomeAggregate", step10, jobRepository, tx))
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.ga.batch.settlement.step;
|
||||
|
||||
import com.ga.batch.settlement.SettlementContext;
|
||||
import com.ga.core.mapper.accounting.ContractAccountingEntryMapper;
|
||||
import com.ga.core.vo.accounting.ContractAccountingEntryVO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
import org.springframework.batch.core.configuration.annotation.JobScope;
|
||||
import org.springframework.batch.core.scope.context.ChunkContext;
|
||||
import org.springframework.batch.core.step.tasklet.Tasklet;
|
||||
import org.springframework.batch.repeat.RepeatStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Step 8b — accountingEntryGenerate: 회계 자동분개
|
||||
*
|
||||
* 정산월 기준 recruit_ledger + maintain_ledger → contract_accounting_entry INSERT.
|
||||
* - 차변(8350 지급수수료) / 대변(2530 미지급금) 한 쌍을 단일 행으로 저장.
|
||||
* - journal_no는 NULL(미전기). 마감 UI에서 채번.
|
||||
* - 이미 분개된 payment는 existsByPaymentId로 중복 방어.
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@JobScope
|
||||
@RequiredArgsConstructor
|
||||
public class AccountingEntryGenerateStep implements Tasklet {
|
||||
|
||||
private final SettlementContext ctx;
|
||||
private final ContractAccountingEntryMapper accountingMapper;
|
||||
|
||||
private static final int BATCH_SIZE = 500;
|
||||
|
||||
@Override
|
||||
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {
|
||||
String settleMonth = ctx.getSettleMonth();
|
||||
log.info("[AccountingEntry] settleMonth={}", settleMonth);
|
||||
|
||||
List<ContractAccountingEntryVO> source = accountingMapper.selectBySettleMonth(settleMonth);
|
||||
if (source.isEmpty()) {
|
||||
log.info("[AccountingEntry] no ledger records for month={}", settleMonth);
|
||||
return RepeatStatus.FINISHED;
|
||||
}
|
||||
|
||||
LocalDate today = LocalDate.now();
|
||||
List<ContractAccountingEntryVO> batch = new ArrayList<>(BATCH_SIZE);
|
||||
int total = 0;
|
||||
|
||||
for (ContractAccountingEntryVO entry : source) {
|
||||
if (entry.getAmount() == null || entry.getAmount().signum() == 0) continue;
|
||||
entry.setEntryDate(today);
|
||||
entry.setJournalNo(null);
|
||||
|
||||
batch.add(entry);
|
||||
if (batch.size() >= BATCH_SIZE) {
|
||||
accountingMapper.insertBatch(batch);
|
||||
total += batch.size();
|
||||
batch.clear();
|
||||
}
|
||||
}
|
||||
if (!batch.isEmpty()) {
|
||||
accountingMapper.insertBatch(batch);
|
||||
total += batch.size();
|
||||
}
|
||||
|
||||
log.info("[AccountingEntry] inserted {} entries for month={}", total, settleMonth);
|
||||
return RepeatStatus.FINISHED;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.ga.batch.settlement.step;
|
||||
|
||||
import com.ga.batch.settlement.SettlementContext;
|
||||
import com.ga.core.mapper.income.AgentAnnualIncomeMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
import org.springframework.batch.core.configuration.annotation.JobScope;
|
||||
import org.springframework.batch.core.scope.context.ChunkContext;
|
||||
import org.springframework.batch.core.step.tasklet.Tasklet;
|
||||
import org.springframework.batch.repeat.RepeatStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Step 9 (conditional) — yearEndIncomeAggregate: 연말 사업소득 집계
|
||||
*
|
||||
* 12월 마감 시에만 실행 (settleMonth ends with "-12" 또는 "12").
|
||||
* payment 테이블 기준으로 해당 연도 설계사별 SUM → agent_annual_income UPSERT.
|
||||
* SQL은 AgentAnnualIncomeMapper.upsertAggregate에 위임.
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@JobScope
|
||||
@RequiredArgsConstructor
|
||||
public class YearEndIncomeAggregateStep implements Tasklet {
|
||||
|
||||
private final SettlementContext ctx;
|
||||
private final AgentAnnualIncomeMapper incomeMapper;
|
||||
|
||||
@Override
|
||||
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {
|
||||
String settleMonth = ctx.getSettleMonth();
|
||||
|
||||
if (!isDecemberClose(settleMonth)) {
|
||||
log.info("[YearEndIncome] skipped — not a December close: {}", settleMonth);
|
||||
return RepeatStatus.FINISHED;
|
||||
}
|
||||
|
||||
int year = extractYear(settleMonth);
|
||||
log.info("[YearEndIncome] aggregating income for year={}", year);
|
||||
|
||||
int rows = incomeMapper.upsertAggregate(year);
|
||||
log.info("[YearEndIncome] agent_annual_income upserted: {} rows", rows);
|
||||
return RepeatStatus.FINISHED;
|
||||
}
|
||||
|
||||
private boolean isDecemberClose(String settleMonth) {
|
||||
if (settleMonth == null) return false;
|
||||
// 형식: yyyyMM (예: 202512) 또는 yyyy-MM
|
||||
return settleMonth.endsWith("12");
|
||||
}
|
||||
|
||||
private int extractYear(String settleMonth) {
|
||||
// yyyyMM → 앞 4자리
|
||||
String digits = settleMonth.replaceAll("[^0-9]", "");
|
||||
return Integer.parseInt(digits.substring(0, 4));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user