feat: 1200%룰 이연 분급 도래월 지급(PayInstallmentStep) + 코드베이스 신입교육 주석

PayInstallmentStep(Step 4.5): 1200%룰로 이연된 분급(installment_plan SCHEDULED)을
도래월(settle_month=정산월)에 recruit_ledger로 편입해 실제 지급. 이전엔 이연만 되고
도래월 지급 절차가 없어 차액이 영구 미지급되던 갭을 메움.
- BatchConfig job flow에 step4b(calcRecruit→payInstallment→calcMaintain) 등록
- InstallmentPlanMapper.resetPaidToScheduledByMonth(+XML)로 재실행 멱등
  (진입 시 PAID→SCHEDULED 역산, Step4 deleteBySettleMonth가 원장 정리)
- RecruitLedgerMapper insert에 reconcile_status 컬럼, SettlementContext.installmentPaidCount
- 단위테스트 3건(정상지급/멱등/계약미존재) GREEN

신입교육 주석: 컨트롤러/서비스/공통/프론트 전반에 도메인·코드 설명 주석 추가
(동작 변경 없음 — 빌드/테스트/타입체크 전부 GREEN으로 무회귀 확인).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
GA Pro
2026-06-12 23:40:28 +09:00
parent 5f43c945f0
commit 17ab1098ec
345 changed files with 4759 additions and 441 deletions
+20
View File
@@ -1,6 +1,19 @@
// ============================================================================
// [공통] HTTP 통신 기반 모듈 — axios 인스턴스 + 응답 래퍼
// ----------------------------------------------------------------------------
// 이 파일은 모든 API 모듈(commission.ts, settle.ts 등)이 공유하는 공통 토대다.
// - 백엔드는 항상 { success, code, message, data } 형태의 JSON(ApiResponse)으로 응답한다.
// - 신입 가이드: 다른 *.ts 의 함수들은 여기서 만든 api 인스턴스와 unwrap() 을 import 해 사용한다.
// ============================================================================
import axios, { AxiosError } from 'axios';
import { message } from 'antd';
// 백엔드 표준 응답 봉투(envelope). 실제 데이터는 data 필드 안에 들어 있다.
// - success: 성공 여부(false 면 비즈니스 오류)
// - code : 응답/오류 코드 문자열
// - message: 사용자에게 보여줄 메시지
// - data : 실제 페이로드(제네릭 T)
// - timestamp: 서버 응답 시각
export interface ApiResponse<T> {
success: boolean;
code: string;
@@ -9,6 +22,9 @@ export interface ApiResponse<T> {
timestamp: string;
}
// 목록 조회용 페이지네이션 응답. list(현재 페이지 데이터) + 페이지 메타.
// - pageNum: 현재 페이지 번호, pageSize: 페이지당 건수
// - total : 전체 건수, pages: 전체 페이지 수
export interface PageResponse<T> {
list: T[];
pageNum: number;
@@ -17,17 +33,20 @@ export interface PageResponse<T> {
pages: number;
}
// axios 인스턴스 생성. baseURL 은 환경변수(VITE_API_URL), 타임아웃 60초.
const api = axios.create({
baseURL: import.meta.env.VITE_API_URL ?? '',
timeout: 60_000,
});
// [요청 인터셉터] 모든 요청에 로그인 토큰(JWT)을 Authorization 헤더로 자동 첨부.
api.interceptors.request.use((config) => {
const token = localStorage.getItem('accessToken');
if (token) config.headers.Authorization = `Bearer ${token}`;
return config;
});
// [응답 인터셉터] 성공 응답이라도 success=false 면 비즈니스 오류로 간주해 토스트 표시.
api.interceptors.response.use(
(res) => {
const body = res.data;
@@ -37,6 +56,7 @@ api.interceptors.response.use(
}
return res;
},
// [HTTP 오류 처리] 401: 토큰 만료/미인증 → 로그인 화면으로. 403: 권한 없음. 그 외: 서버 오류.
(err: AxiosError<ApiResponse<unknown>>) => {
const status = err.response?.status;
const body = err.response?.data;