feat: ga-core + ga-api + ga-batch + V13-V15 + Docker + frontend skeleton
ga-core (22 tables): - user/role: VO + Mapper + XML (login + RBAC) - org: Grade, Organization (CTE recursive tree), Agent + history - product: InsuranceCompany, Product, Contract - rule: CommissionRate, PayoutRule, OverrideRule, ChargebackRule, ExceptionTypeCode - receive: CompanyProfile, FieldMapping, CodeMapping, RawCommissionData, ParseError - ledger: Recruit, Maintain, Exception (batch insert + cursor) - settle: SettleMaster (UPSERT), Override, Chargeback, Payment, Reconciliation, BatchJobLog - All XMLs use EncryptTypeHandler for PII fields ga-api: - AuthController: login (lock on N fails), refresh, me, password, logout - Domain controllers: Agent, Organization, Contract, Company, Product, Rule, Receive, Ledger (recruit/maintain/exception + approve), Settle (confirm/hold/release), Payment, User, Role (matrix), BatchTrigger - All use @RequirePermission + @DataChangeLog ga-batch: - MonthlySettlementJob: 8 Steps (receiveData, transformData, reconcile, calcRecruit, calcMaintain, chargebackException, calcOverride, aggregate) - DataReceiver strategy + ManualReceiver, MappingEngine stub - CommissionCalculator (pct/tax via MoneyUtil), JobLauncherController DB V13~V15: - V13: 14 indexes (settle_master, ledgers, contract, chargeback, payment, raw, logs) - V14: create_monthly_partition() helper function - V15: v_agent_monthly_summary, v_org_settle_summary, v_chargeback_risk views Infra: - docker-compose: postgres + redis + api + batch + frontend - Multi-stage Dockerfile (gradle build → JRE) ga-frontend (skeleton): - Vite + React 18 + TypeScript + AntD 5 + AG Grid + React Query + Zustand - API request layer (axios + JWT), LoginPage, MainLayout (dynamic menu), Dashboard - nginx.conf for /api proxying
This commit is contained in:
+33
@@ -0,0 +1,33 @@
|
|||||||
|
# Multi-module Spring Boot 빌드 (ga-api 또는 ga-batch 선택)
|
||||||
|
# Build: docker build --build-arg MODULE=ga-api -t ga-api .
|
||||||
|
ARG MODULE=ga-api
|
||||||
|
|
||||||
|
# Stage 1: build
|
||||||
|
FROM gradle:8.6-jdk17 AS builder
|
||||||
|
WORKDIR /work
|
||||||
|
COPY settings.gradle build.gradle gradle.properties ./
|
||||||
|
COPY ga-common/build.gradle ga-common/
|
||||||
|
COPY ga-core/build.gradle ga-core/
|
||||||
|
COPY ga-api/build.gradle ga-api/
|
||||||
|
COPY ga-batch/build.gradle ga-batch/
|
||||||
|
COPY ga-admin/build.gradle ga-admin/
|
||||||
|
# 의존성 사전 다운로드 (캐시 최적화)
|
||||||
|
RUN gradle --no-daemon dependencies > /dev/null 2>&1 || true
|
||||||
|
|
||||||
|
COPY ga-common ga-common
|
||||||
|
COPY ga-core ga-core
|
||||||
|
COPY ga-api ga-api
|
||||||
|
COPY ga-batch ga-batch
|
||||||
|
COPY ga-admin ga-admin
|
||||||
|
|
||||||
|
ARG MODULE
|
||||||
|
RUN gradle --no-daemon :${MODULE}:bootJar -x test
|
||||||
|
|
||||||
|
# Stage 2: runtime
|
||||||
|
FROM eclipse-temurin:17-jre-alpine
|
||||||
|
ARG MODULE
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=builder /work/${MODULE}/build/libs/${MODULE}.jar /app/app.jar
|
||||||
|
ENV TZ=Asia/Seoul
|
||||||
|
EXPOSE 8080 8081
|
||||||
|
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# GA 수수료 정산 솔루션
|
# GA 수수료 정산 솔루션
|
||||||
|
|
||||||
법인보험대리점(GA) 수수료 정산 시스템.
|
법인보험대리점(GA) 수수료 정산 시스템 — Multi-module Spring Boot + React.
|
||||||
|
|
||||||
## 기술 스택
|
## 기술 스택
|
||||||
|
|
||||||
@@ -8,106 +8,147 @@
|
|||||||
|------|------|
|
|------|------|
|
||||||
| Backend | Java 17, Spring Boot 3.2, Spring Security, Spring Batch |
|
| Backend | Java 17, Spring Boot 3.2, Spring Security, Spring Batch |
|
||||||
| ORM | MyBatis 3 + PageHelper |
|
| ORM | MyBatis 3 + PageHelper |
|
||||||
| DB | PostgreSQL 14+ |
|
| DB | PostgreSQL 16 |
|
||||||
| Cache | Redis 7 |
|
| Cache | Redis 7 |
|
||||||
| Build | Gradle 멀티모듈 |
|
| Build | Gradle 멀티모듈 |
|
||||||
| Frontend | (예정) React 18 + Ant Design 5 + AG Grid |
|
| Frontend | React 18 + Vite + TypeScript + Ant Design 5 + AG Grid + React Query + Zustand |
|
||||||
|
| Infra | Docker Compose, Nginx |
|
||||||
|
|
||||||
## 모듈 구조
|
## 모듈 구조
|
||||||
|
|
||||||
```
|
```
|
||||||
ga-commission-system/
|
ga-commission-system/
|
||||||
├── ga-common/ # 공통 프레임워크 (인증/권한/로그/엑셀/공통코드/메뉴 등)
|
├── ga-common/ # 공통 프레임워크 (인증/권한/로그/엑셀/공통코드/메뉴)
|
||||||
├── ga-core/ # 도메인 코어 (VO, Mapper, SQL) — 예정
|
├── ga-core/ # 도메인 코어 (VO, Mapper, SQL)
|
||||||
├── ga-api/ # REST API (실행 모듈)
|
├── ga-api/ # REST API (포트 8080)
|
||||||
├── ga-batch/ # Spring Batch (실행 모듈) — 예정
|
├── ga-batch/ # Spring Batch (포트 8081)
|
||||||
└── ga-admin/ # 관리자 기능 — 예정
|
├── ga-admin/ # 관리자 라이브러리
|
||||||
|
└── ga-frontend/ # React SPA (포트 3000)
|
||||||
```
|
```
|
||||||
|
|
||||||
## 진행 상황
|
## 빠른 시작 (Docker Compose)
|
||||||
|
|
||||||
- [x] 1단계 — 프로젝트 뼈대 + DB 마이그레이션
|
|
||||||
- [x] Gradle 멀티모듈 + 5개 모듈
|
|
||||||
- [x] Flyway V1~V12 (조직/상품/규정/수신/원장/정산/배치/공통코드/메뉴/시스템/초기데이터)
|
|
||||||
|
|
||||||
- [x] **ga-common — 공통 프레임워크 (완성)**
|
|
||||||
- [x] `model/` — ApiResponse, PageResponse, SearchParam, TreeNode
|
|
||||||
- [x] `exception/` — ErrorCode, BizException, GlobalExceptionHandler
|
|
||||||
- [x] `annotation/` — @RequirePermission, @DataChangeLog, @ExcelColumn, @Mask
|
|
||||||
- [x] `aop/` — Permission/DataChangeLog/ApiLog Aspect
|
|
||||||
- [x] `auth/` — JwtTokenProvider, JwtAuthFilter, LoginUser
|
|
||||||
- [x] `security/` — SecurityConfig, JwtAuthEntryPoint, AccessDeniedHandler
|
|
||||||
- [x] `config/` — MyBatis/Redis/Swagger/Web/Async Config
|
|
||||||
- [x] `mybatis/` — BaseMapper, EncryptTypeHandler, JsonTypeHandler, AuditInterceptor
|
|
||||||
- [x] `code/` — CommonCodeService(Redis 캐시) + Controller + Mapper
|
|
||||||
- [x] `menu/` — MenuService, PermissionChecker, MenuTreeBuilder + Controller
|
|
||||||
- [x] `excel/` — 대용량 Export(SXSSF+Cursor) / Import(SAX+배치)
|
|
||||||
- [x] `file/` — FileService + Controller
|
|
||||||
- [x] `system/` — SystemConfig, DataChangeLog, ApiAccessLog Service
|
|
||||||
- [x] `notification/` — NotificationService + Controller
|
|
||||||
- [x] `util/` — DateUtil, MoneyUtil, MaskUtil, EncryptUtil, SecurityUtil
|
|
||||||
- [x] `grid/` — GridSearchParam, GridResponse (AG Grid 서버사이드)
|
|
||||||
|
|
||||||
- [ ] ga-core (예정) — 22개 테이블의 VO/Mapper/XML
|
|
||||||
- [ ] ga-api (예정) — Controller + Service
|
|
||||||
- [ ] ga-batch (예정) — MonthlySettlementJob 8 Step
|
|
||||||
- [ ] ga-frontend (예정) — React + AntD + AG Grid
|
|
||||||
|
|
||||||
## 실행 (DB만 우선 검증)
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# PostgreSQL & Redis 기동 후 (예: docker compose)
|
docker compose up -d --build
|
||||||
psql -h localhost -U ga -c "CREATE DATABASE ga"
|
# 프론트: http://localhost:3000
|
||||||
|
# API: http://localhost:8080/swagger-ui.html
|
||||||
|
# Batch: http://localhost:8081 (내부)
|
||||||
|
```
|
||||||
|
|
||||||
# Flyway 마이그레이션은 ga-api 부팅 시 자동 실행됨
|
## 로컬 개발
|
||||||
# (ga-api의 spring.datasource 와 Flyway 설정에 따름)
|
|
||||||
|
### 1. PostgreSQL + Redis 기동
|
||||||
|
```bash
|
||||||
|
docker compose up -d postgres redis
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. ga-api 실행 (Flyway 자동 마이그레이션)
|
||||||
|
```bash
|
||||||
./gradlew :ga-api:bootRun
|
./gradlew :ga-api:bootRun
|
||||||
```
|
```
|
||||||
|
|
||||||
## 핵심 사용 예시
|
### 3. ga-batch 실행
|
||||||
|
```bash
|
||||||
|
./gradlew :ga-batch:bootRun
|
||||||
|
```
|
||||||
|
|
||||||
### 1) 컨트롤러 표준 패턴
|
### 4. 프론트 실행
|
||||||
|
```bash
|
||||||
|
cd ga-frontend
|
||||||
|
npm install
|
||||||
|
npm run dev
|
||||||
|
# http://localhost:3000
|
||||||
|
```
|
||||||
|
|
||||||
|
## 주요 기능
|
||||||
|
|
||||||
|
### 백엔드
|
||||||
|
- **인증/권한**: JWT (HS256), 메뉴×권한 RBAC, BCrypt 비밀번호
|
||||||
|
- **공통코드**: Redis 캐시, 시스템코드 잠금, REST CRUD
|
||||||
|
- **메뉴**: 트리 구조, 사용자별 동적 메뉴 + 권한 매트릭스
|
||||||
|
- **엑셀**: SXSSF+Cursor 다운로드(100만건), SAX+배치 업로드(70만건)
|
||||||
|
- **로그**: 로그인/API/데이터변경 자동 기록 (비동기)
|
||||||
|
- **암호화**: AES-256 CBC (resident_no, account_no)
|
||||||
|
|
||||||
|
### 도메인
|
||||||
|
- **조직**: 트리 구조, CTE 재귀, 인사 이력
|
||||||
|
- **계약**: 보험사/상품/계약 관리
|
||||||
|
- **수수료 규정**: 보험사율, 지급율, 오버라이드, 환수, 예외코드
|
||||||
|
- **원장**: 모집/유지/예외 (PageHelper, AG Grid 서버사이드 가능)
|
||||||
|
- **정산**: 8 Step 배치 (수신→변환→대사→모집→유지→환수/예외→오버라이드→집계)
|
||||||
|
- **지급**: 이체파일 생성, 상태 추적
|
||||||
|
|
||||||
|
### 정산 배치 8 Step
|
||||||
|
1. **receiveData** — 보험사별 DataReceiver → raw_commission_data 적재
|
||||||
|
2. **transformData** — MappingEngine으로 표준 변환
|
||||||
|
3. **reconcile** — 보험사 통보액 vs 시스템 계산액 대사
|
||||||
|
4. **calcRecruit** — 모집수수료 (계약시점 규정 적용)
|
||||||
|
5. **calcMaintain** — 유지수수료 (납입주기 매칭)
|
||||||
|
6. **chargebackException** — 환수 자동생성
|
||||||
|
7. **calcOverride** — 조직 트리 오버라이드
|
||||||
|
8. **aggregate** — 통합 집계 → settle_master UPSERT (3.3% 세금)
|
||||||
|
|
||||||
|
## 신입 개발자 표준 패턴
|
||||||
|
|
||||||
|
### Backend (5분)
|
||||||
```java
|
```java
|
||||||
@RestController @RequestMapping("/api/agents")
|
// 1) VO (XxxVO + XxxResp + XxxSaveReq + XxxSearchParam)
|
||||||
@RequiredArgsConstructor
|
@Data
|
||||||
public class AgentController {
|
public class FooSearchParam extends SearchParam { }
|
||||||
private final AgentService service;
|
|
||||||
|
|
||||||
|
// 2) Mapper Interface + XML
|
||||||
|
@Mapper
|
||||||
|
public interface FooMapper {
|
||||||
|
List<FooResp> selectList(FooSearchParam p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3) Service
|
||||||
|
@Service @RequiredArgsConstructor
|
||||||
|
public class FooService {
|
||||||
|
private final FooMapper mapper;
|
||||||
|
public PageResponse<FooResp> list(FooSearchParam p) {
|
||||||
|
p.startPage();
|
||||||
|
return PageResponse.of(mapper.selectList(p));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4) Controller
|
||||||
|
@RestController @RequestMapping("/api/foos") @RequiredArgsConstructor
|
||||||
|
public class FooController {
|
||||||
|
private final FooService service;
|
||||||
@GetMapping
|
@GetMapping
|
||||||
@RequirePermission(menu = "ORG_AGENT", perm = "READ")
|
@RequirePermission(menu = "FOO", perm = "READ")
|
||||||
public ApiResponse<PageResponse<AgentResp>> list(AgentSearchParam param) {
|
public ApiResponse<PageResponse<FooResp>> list(FooSearchParam p) {
|
||||||
return ApiResponse.ok(service.list(param));
|
return ApiResponse.ok(service.list(p));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2) 서비스 표준 패턴
|
|
||||||
```java
|
|
||||||
public PageResponse<AgentResp> list(AgentSearchParam param) {
|
|
||||||
param.startPage(); // PageHelper
|
|
||||||
return PageResponse.of(mapper.selectList(param));
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3) 공통코드 검증
|
|
||||||
```java
|
|
||||||
codeService.validateCode("AGENT_STATUS", req.getStatus()); // 없으면 예외
|
|
||||||
String name = codeService.getCodeName("AGENT_STATUS", "ACTIVE"); // "정상"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4) 대용량 엑셀 다운로드
|
|
||||||
```java
|
|
||||||
@GetMapping("/export")
|
|
||||||
@RequirePermission(menu = "LEDGER", perm = "EXPORT")
|
|
||||||
public void export(LedgerSearchParam param, HttpServletResponse res) {
|
|
||||||
excelService.exportLargeExcel("모집수수료원장",
|
|
||||||
RecruitLedgerExcelVO.class,
|
|
||||||
() -> ledgerMapper.selectCursor(param), res);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 보안
|
## 보안
|
||||||
|
|
||||||
- JWT (HS256) — 액세스 2시간, 리프레시 7일
|
- JWT (HS256) — 액세스 2시간, 리프레시 7일
|
||||||
- AES-256 CBC 암호화 (`resident_no`, `account_no`) — `EncryptTypeHandler` 자동 적용
|
- AES-256 CBC 암호화 (자동 적용 — `EncryptTypeHandler`)
|
||||||
- RBAC — `role_menu_permission` 기반 메뉴×권한 매트릭스
|
- RBAC — `role_menu_permission` 매트릭스
|
||||||
|
- `@DataChangeLog` — 데이터 변경 이력 AOP 자동
|
||||||
|
- API 접근 로그 — 비동기 저장
|
||||||
|
|
||||||
|
## DB 마이그레이션
|
||||||
|
- V1~V8: 도메인 스키마 + 초기 데이터
|
||||||
|
- V9~V12: 공통코드/메뉴/시스템관리 + 초기 데이터
|
||||||
|
- V13: 인덱스 최적화
|
||||||
|
- V14: 파티셔닝 helper 함수
|
||||||
|
- V15: 통계/리포트 뷰 (v_agent_monthly_summary, v_org_settle_summary, v_chargeback_risk)
|
||||||
|
|
||||||
|
## 진행 상황
|
||||||
|
|
||||||
|
- [x] 1단계 — 프로젝트 뼈대 + DB (V1~V15)
|
||||||
|
- [x] **ga-common** — 공통 프레임워크 (model/exception/auth/security/code/menu/excel/file/system/notification)
|
||||||
|
- [x] **ga-core** — 22개 테이블의 VO/Mapper/XML
|
||||||
|
- [x] **ga-api** — Auth + 도메인 컨트롤러 (org/product/rule/receive/ledger/settle/payment/system)
|
||||||
|
- [x] **ga-batch** — MonthlySettlementJob 8 Step
|
||||||
|
- [x] **Docker** — docker-compose + multi-stage Dockerfile + nginx
|
||||||
|
- [x] **ga-frontend** — Vite + React + AntD + React Query 스켈레톤 (로그인 + 대시보드 + 동적 메뉴)
|
||||||
|
|
||||||
|
## 향후 확장
|
||||||
|
- **프론트 페이지 추가**: menu 테이블 등록 → React 페이지 구현 → 동적 라우팅 적용
|
||||||
|
- **신규 보험사**: company_profile + company_field_mapping 등록 + DataReceiver 구현
|
||||||
|
- **신규 정산 규정**: commission_rate/payout_rule/override_rule/chargeback_rule 의 effective_from 만 다른 버전으로 추가
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
container_name: ga-postgres
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: ga
|
||||||
|
POSTGRES_USER: ga
|
||||||
|
POSTGRES_PASSWORD: ga
|
||||||
|
TZ: Asia/Seoul
|
||||||
|
ports:
|
||||||
|
- "5432:5432"
|
||||||
|
volumes:
|
||||||
|
- ga-postgres-data:/var/lib/postgresql/data
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U ga -d ga"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:7-alpine
|
||||||
|
container_name: ga-redis
|
||||||
|
ports:
|
||||||
|
- "6379:6379"
|
||||||
|
volumes:
|
||||||
|
- ga-redis-data:/data
|
||||||
|
|
||||||
|
api:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
args:
|
||||||
|
MODULE: ga-api
|
||||||
|
container_name: ga-api
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_started
|
||||||
|
environment:
|
||||||
|
SPRING_PROFILES_ACTIVE: docker
|
||||||
|
SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/ga
|
||||||
|
SPRING_DATASOURCE_USERNAME: ga
|
||||||
|
SPRING_DATASOURCE_PASSWORD: ga
|
||||||
|
SPRING_DATA_REDIS_HOST: redis
|
||||||
|
JWT_SECRET: ${JWT_SECRET:-please-change-this-jwt-secret-must-be-256-bits}
|
||||||
|
ENCRYPT_KEY: ${ENCRYPT_KEY:-please-change-this-32-byte-key!}
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
|
||||||
|
batch:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
args:
|
||||||
|
MODULE: ga-batch
|
||||||
|
container_name: ga-batch
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_started
|
||||||
|
api:
|
||||||
|
condition: service_started
|
||||||
|
environment:
|
||||||
|
SPRING_PROFILES_ACTIVE: docker
|
||||||
|
SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/ga
|
||||||
|
SPRING_DATASOURCE_USERNAME: ga
|
||||||
|
SPRING_DATASOURCE_PASSWORD: ga
|
||||||
|
SPRING_DATA_REDIS_HOST: redis
|
||||||
|
JWT_SECRET: ${JWT_SECRET:-please-change-this-jwt-secret-must-be-256-bits}
|
||||||
|
ENCRYPT_KEY: ${ENCRYPT_KEY:-please-change-this-32-byte-key!}
|
||||||
|
ports:
|
||||||
|
- "8081:8081"
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
build:
|
||||||
|
context: ./ga-frontend
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: ga-frontend
|
||||||
|
depends_on:
|
||||||
|
- api
|
||||||
|
ports:
|
||||||
|
- "3000:80"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
ga-postgres-data:
|
||||||
|
ga-redis-data:
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package com.ga.api.auth;
|
||||||
|
|
||||||
|
import com.ga.common.model.ApiResponse;
|
||||||
|
import com.ga.core.vo.user.UserResp;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Tag(name = "인증")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/auth")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AuthController {
|
||||||
|
|
||||||
|
private final AuthService authService;
|
||||||
|
|
||||||
|
@PostMapping("/login")
|
||||||
|
public ApiResponse<LoginResp> login(@Valid @RequestBody LoginReq req, HttpServletRequest http) {
|
||||||
|
return ApiResponse.ok(authService.login(req, ipOf(http), http.getHeader("User-Agent")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/refresh")
|
||||||
|
public ApiResponse<LoginResp> refresh(@RequestBody Map<String, String> body) {
|
||||||
|
return ApiResponse.ok(authService.refresh(body.get("refreshToken")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/me")
|
||||||
|
public ApiResponse<UserResp> me() {
|
||||||
|
return ApiResponse.ok(authService.me());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/password")
|
||||||
|
public ApiResponse<Void> changePassword(@RequestBody Map<String, String> body) {
|
||||||
|
authService.changePassword(body.get("oldPassword"), body.get("newPassword"));
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/logout")
|
||||||
|
public ApiResponse<Void> logout() {
|
||||||
|
authService.logout();
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String ipOf(HttpServletRequest req) {
|
||||||
|
String xf = req.getHeader("X-Forwarded-For");
|
||||||
|
if (xf != null && !xf.isBlank()) return xf.split(",")[0].trim();
|
||||||
|
return req.getRemoteAddr();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
package com.ga.api.auth;
|
||||||
|
|
||||||
|
import com.ga.common.auth.JwtTokenProvider;
|
||||||
|
import com.ga.common.exception.BizException;
|
||||||
|
import com.ga.common.exception.ErrorCode;
|
||||||
|
import com.ga.common.system.SystemConfigService;
|
||||||
|
import com.ga.common.system.SystemLogMapper;
|
||||||
|
import com.ga.common.util.SecurityUtil;
|
||||||
|
import com.ga.core.mapper.user.UserMapper;
|
||||||
|
import com.ga.core.vo.user.UserResp;
|
||||||
|
import com.ga.core.vo.user.UserVO;
|
||||||
|
import io.jsonwebtoken.Claims;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AuthService {
|
||||||
|
|
||||||
|
private final UserMapper userMapper;
|
||||||
|
private final PasswordEncoder passwordEncoder;
|
||||||
|
private final JwtTokenProvider tokenProvider;
|
||||||
|
private final SystemConfigService configService;
|
||||||
|
private final SystemLogMapper systemLogMapper;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public LoginResp login(LoginReq req, String ipAddress, String userAgent) {
|
||||||
|
UserVO user = userMapper.selectByLoginId(req.getLoginId());
|
||||||
|
int failLimit = configService.getInt("LOGIN_FAIL_LIMIT", 5);
|
||||||
|
|
||||||
|
if (user == null) {
|
||||||
|
saveLoginLog(null, req.getLoginId(), "FAIL", "USER_NOT_FOUND", ipAddress, userAgent);
|
||||||
|
throw new BizException(ErrorCode.LOGIN_FAIL);
|
||||||
|
}
|
||||||
|
if ("LOCKED".equals(user.getStatus())) {
|
||||||
|
saveLoginLog(user.getUserId(), req.getLoginId(), "FAIL", "ACCOUNT_LOCKED", ipAddress, userAgent);
|
||||||
|
throw new BizException(ErrorCode.ACCOUNT_LOCKED);
|
||||||
|
}
|
||||||
|
if ("RETIRED".equals(user.getStatus())) {
|
||||||
|
throw new BizException(ErrorCode.ACCOUNT_RETIRED);
|
||||||
|
}
|
||||||
|
if (!passwordEncoder.matches(req.getPassword(), user.getPassword())) {
|
||||||
|
userMapper.incrementFailCount(user.getUserId());
|
||||||
|
int newFail = (user.getFailCount() == null ? 0 : user.getFailCount()) + 1;
|
||||||
|
if (newFail >= failLimit) {
|
||||||
|
userMapper.updateStatus(user.getUserId(), "LOCKED");
|
||||||
|
}
|
||||||
|
saveLoginLog(user.getUserId(), req.getLoginId(), "FAIL", "BAD_PASSWORD", ipAddress, userAgent);
|
||||||
|
throw new BizException(ErrorCode.LOGIN_FAIL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 로그인 성공
|
||||||
|
userMapper.updateLastLogin(user.getUserId());
|
||||||
|
List<String> roles = userMapper.selectRoleCodes(user.getUserId());
|
||||||
|
String access = tokenProvider.createAccessToken(user.getUserId(), user.getLoginId(), roles);
|
||||||
|
String refresh = tokenProvider.createRefreshToken(user.getUserId(), user.getLoginId());
|
||||||
|
saveLoginLog(user.getUserId(), req.getLoginId(), "SUCCESS", null, ipAddress, userAgent);
|
||||||
|
|
||||||
|
return LoginResp.builder()
|
||||||
|
.accessToken(access)
|
||||||
|
.refreshToken(refresh)
|
||||||
|
.accessTokenExpiresIn(tokenProvider.getAccessExpire())
|
||||||
|
.userId(user.getUserId())
|
||||||
|
.loginId(user.getLoginId())
|
||||||
|
.userName(user.getUserName())
|
||||||
|
.roles(roles)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoginResp refresh(String refreshToken) {
|
||||||
|
Claims claims = tokenProvider.parse(refreshToken);
|
||||||
|
if (!"REFRESH".equals(claims.get("type"))) {
|
||||||
|
throw new BizException(ErrorCode.TOKEN_INVALID);
|
||||||
|
}
|
||||||
|
Long userId = claims.get("uid", Long.class);
|
||||||
|
UserVO user = userMapper.selectById(userId);
|
||||||
|
if (user == null || !"ACTIVE".equals(user.getStatus())) {
|
||||||
|
throw new BizException(ErrorCode.UNAUTHORIZED);
|
||||||
|
}
|
||||||
|
List<String> roles = userMapper.selectRoleCodes(userId);
|
||||||
|
String newAccess = tokenProvider.createAccessToken(userId, user.getLoginId(), roles);
|
||||||
|
return LoginResp.builder()
|
||||||
|
.accessToken(newAccess)
|
||||||
|
.accessTokenExpiresIn(tokenProvider.getAccessExpire())
|
||||||
|
.userId(userId)
|
||||||
|
.loginId(user.getLoginId())
|
||||||
|
.userName(user.getUserName())
|
||||||
|
.roles(roles)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserResp me() {
|
||||||
|
Long userId = SecurityUtil.getCurrentUserId();
|
||||||
|
if (userId == null) throw new BizException(ErrorCode.UNAUTHORIZED);
|
||||||
|
UserResp resp = userMapper.selectDetailById(userId);
|
||||||
|
if (resp == null) throw new BizException(ErrorCode.NOT_FOUND);
|
||||||
|
resp.setRoleCodes(userMapper.selectRoleCodes(userId));
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void changePassword(String oldPassword, String newPassword) {
|
||||||
|
Long userId = SecurityUtil.getCurrentUserId();
|
||||||
|
if (userId == null) throw new BizException(ErrorCode.UNAUTHORIZED);
|
||||||
|
UserVO user = userMapper.selectById(userId);
|
||||||
|
if (!passwordEncoder.matches(oldPassword, user.getPassword())) {
|
||||||
|
throw new BizException(ErrorCode.LOGIN_FAIL, "기존 비밀번호가 일치하지 않습니다");
|
||||||
|
}
|
||||||
|
validatePasswordPolicy(newPassword);
|
||||||
|
userMapper.updatePassword(userId, passwordEncoder.encode(newPassword));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void logout() {
|
||||||
|
Long userId = SecurityUtil.getCurrentUserId();
|
||||||
|
saveLoginLog(userId, SecurityUtil.getCurrentLoginId(), "LOGOUT", null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveLoginLog(Long userId, String loginId, String type, String reason, String ip, String ua) {
|
||||||
|
try {
|
||||||
|
systemLogMapper.insertLoginLog(userId, loginId, type, reason, ip, ua);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("Login log save failed: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validatePasswordPolicy(String password) {
|
||||||
|
int min = configService.getInt("PASSWORD_MIN_LEN", 8);
|
||||||
|
if (password == null || password.length() < min) {
|
||||||
|
throw new BizException(ErrorCode.PASSWORD_POLICY,
|
||||||
|
"비밀번호는 " + min + "자 이상이어야 합니다");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.ga.api.auth;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class LoginReq {
|
||||||
|
@NotBlank(message = "아이디는 필수입니다")
|
||||||
|
private String loginId;
|
||||||
|
@NotBlank(message = "비밀번호는 필수입니다")
|
||||||
|
private String password;
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.ga.api.auth;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
public class LoginResp {
|
||||||
|
private String accessToken;
|
||||||
|
private String refreshToken;
|
||||||
|
private long accessTokenExpiresIn; // ms
|
||||||
|
private Long userId;
|
||||||
|
private String loginId;
|
||||||
|
private String userName;
|
||||||
|
private List<String> roles;
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package com.ga.api.controller.batch;
|
||||||
|
|
||||||
|
import com.ga.common.annotation.RequirePermission;
|
||||||
|
import com.ga.common.exception.BizException;
|
||||||
|
import com.ga.common.exception.ErrorCode;
|
||||||
|
import com.ga.common.model.ApiResponse;
|
||||||
|
import com.ga.core.mapper.settle.BatchJobLogMapper;
|
||||||
|
import com.ga.core.vo.settle.BatchJobLogVO;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 배치 트리거 — 실제 배치 잡 실행은 ga-batch 모듈의 별도 Spring Boot 프로세스에서 수행.
|
||||||
|
* ga-api 에서는 batch_job_log 만 조회/요청 등록.
|
||||||
|
*
|
||||||
|
* 운영 환경에서는 메시지 큐(Kafka, Redis Streams) 또는
|
||||||
|
* Spring Batch JobLauncher REST API 로 ga-batch 프로세스를 호출하는 것을 권장.
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Tag(name = "배치")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/batch")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class BatchTriggerController {
|
||||||
|
|
||||||
|
private final BatchJobLogMapper jobLogMapper;
|
||||||
|
|
||||||
|
/** 정산 배치 시작 요청 (실제 실행은 ga-batch 프로세스가 폴링/큐) */
|
||||||
|
@PostMapping("/settlement/run")
|
||||||
|
@RequirePermission(menu = "BATCH_RUN", perm = "APPROVE")
|
||||||
|
public ApiResponse<Long> runSettlement(@RequestBody Map<String, String> body) {
|
||||||
|
String settleMonth = body.get("settleMonth");
|
||||||
|
if (settleMonth == null || !settleMonth.matches("\\d{6}")) {
|
||||||
|
throw new BizException(ErrorCode.INVALID_PARAMETER, "settleMonth (yyyyMM) 가 필요합니다");
|
||||||
|
}
|
||||||
|
if (jobLogMapper.isRunning("MONTHLY_SETTLEMENT") > 0) {
|
||||||
|
throw new BizException(ErrorCode.BATCH_RUNNING);
|
||||||
|
}
|
||||||
|
BatchJobLogVO log = new BatchJobLogVO();
|
||||||
|
log.setJobName("MONTHLY_SETTLEMENT");
|
||||||
|
log.setSettleMonth(settleMonth);
|
||||||
|
log.setStatus("REQUESTED");
|
||||||
|
log.setTriggeredBy(com.ga.common.util.SecurityUtil.getCurrentUserId());
|
||||||
|
jobLogMapper.insert(log);
|
||||||
|
return ApiResponse.ok(log.getJobId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/status")
|
||||||
|
@RequirePermission(menu = "BATCH_RUN", perm = "READ")
|
||||||
|
public ApiResponse<BatchJobLogVO> latest(@RequestParam(defaultValue = "MONTHLY_SETTLEMENT") String jobName) {
|
||||||
|
return ApiResponse.ok(jobLogMapper.selectLatest(jobName));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/jobs")
|
||||||
|
@RequirePermission(menu = "BATCH_RUN", perm = "READ")
|
||||||
|
public ApiResponse<List<BatchJobLogVO>> list(@RequestParam(required = false) String jobName,
|
||||||
|
@RequestParam(required = false) String settleMonth) {
|
||||||
|
return ApiResponse.ok(jobLogMapper.selectList(jobName, settleMonth));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/jobs/{jobId}")
|
||||||
|
@RequirePermission(menu = "BATCH_RUN", perm = "READ")
|
||||||
|
public ApiResponse<BatchJobLogVO> detail(@PathVariable Long jobId) {
|
||||||
|
return ApiResponse.ok(jobLogMapper.selectById(jobId));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
package com.ga.api.controller.ledger;
|
||||||
|
|
||||||
|
import com.ga.api.service.ledger.LedgerService;
|
||||||
|
import com.ga.common.annotation.DataChangeLog;
|
||||||
|
import com.ga.common.annotation.RequirePermission;
|
||||||
|
import com.ga.common.excel.ExcelService;
|
||||||
|
import com.ga.common.model.ApiResponse;
|
||||||
|
import com.ga.common.model.PageResponse;
|
||||||
|
import com.ga.core.mapper.ledger.MaintainLedgerMapper;
|
||||||
|
import com.ga.core.mapper.ledger.RecruitLedgerMapper;
|
||||||
|
import com.ga.core.vo.ledger.*;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Tag(name = "원장 관리")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/ledger")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class LedgerController {
|
||||||
|
|
||||||
|
private final LedgerService service;
|
||||||
|
private final RecruitLedgerMapper recruitMapper;
|
||||||
|
private final MaintainLedgerMapper maintainMapper;
|
||||||
|
private final ExcelService excelService;
|
||||||
|
|
||||||
|
// ===== 모집 =====
|
||||||
|
@GetMapping("/recruit")
|
||||||
|
@RequirePermission(menu = "LEDGER_RECRUIT", perm = "READ")
|
||||||
|
public ApiResponse<PageResponse<LedgerResp>> listRecruit(@ModelAttribute LedgerSearchParam param) {
|
||||||
|
return ApiResponse.ok(service.listRecruit(param));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/recruit/{ledgerId}")
|
||||||
|
@RequirePermission(menu = "LEDGER_RECRUIT", perm = "READ")
|
||||||
|
public ApiResponse<LedgerResp> detailRecruit(@PathVariable Long ledgerId) {
|
||||||
|
return ApiResponse.ok(service.detailRecruit(ledgerId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/recruit/export")
|
||||||
|
@RequirePermission(menu = "LEDGER_RECRUIT", perm = "EXPORT")
|
||||||
|
public void exportRecruit(@ModelAttribute LedgerSearchParam param, HttpServletResponse res) {
|
||||||
|
excelService.exportLargeExcel("모집수수료원장", LedgerExcelVO.class,
|
||||||
|
() -> recruitMapper.selectCursorForExcel(param), res);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 유지 =====
|
||||||
|
@GetMapping("/maintain")
|
||||||
|
@RequirePermission(menu = "LEDGER_MAINTAIN", perm = "READ")
|
||||||
|
public ApiResponse<PageResponse<LedgerResp>> listMaintain(@ModelAttribute LedgerSearchParam param) {
|
||||||
|
return ApiResponse.ok(service.listMaintain(param));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/maintain/{ledgerId}")
|
||||||
|
@RequirePermission(menu = "LEDGER_MAINTAIN", perm = "READ")
|
||||||
|
public ApiResponse<LedgerResp> detailMaintain(@PathVariable Long ledgerId) {
|
||||||
|
return ApiResponse.ok(service.detailMaintain(ledgerId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/maintain/export")
|
||||||
|
@RequirePermission(menu = "LEDGER_MAINTAIN", perm = "EXPORT")
|
||||||
|
public void exportMaintain(@ModelAttribute LedgerSearchParam param, HttpServletResponse res) {
|
||||||
|
excelService.exportLargeExcel("유지수수료원장", LedgerExcelVO.class,
|
||||||
|
() -> maintainMapper.selectCursorForExcel(param), res);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 예외 =====
|
||||||
|
@GetMapping("/exception")
|
||||||
|
@RequirePermission(menu = "LEDGER_EXCEPTION", perm = "READ")
|
||||||
|
public ApiResponse<PageResponse<ExceptionLedgerResp>> listException(@ModelAttribute LedgerSearchParam param) {
|
||||||
|
return ApiResponse.ok(service.listException(param));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/exception/{ledgerId}")
|
||||||
|
@RequirePermission(menu = "LEDGER_EXCEPTION", perm = "READ")
|
||||||
|
public ApiResponse<ExceptionLedgerResp> detailException(@PathVariable Long ledgerId) {
|
||||||
|
return ApiResponse.ok(service.detailException(ledgerId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/exception")
|
||||||
|
@RequirePermission(menu = "LEDGER_EXCEPTION", perm = "CREATE")
|
||||||
|
@DataChangeLog(menu = "LEDGER_EXCEPTION", table = "exception_ledger")
|
||||||
|
public ApiResponse<Long> createException(@Valid @RequestBody ExceptionLedgerSaveReq req) {
|
||||||
|
return ApiResponse.ok(service.createException(req));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/exception/{ledgerId}/approve")
|
||||||
|
@RequirePermission(menu = "LEDGER_EXCEPTION", perm = "APPROVE")
|
||||||
|
@DataChangeLog(menu = "LEDGER_EXCEPTION", table = "exception_ledger")
|
||||||
|
public ApiResponse<Void> approveException(@PathVariable Long ledgerId,
|
||||||
|
@RequestBody Map<String, String> body) {
|
||||||
|
service.approveException(ledgerId, body.get("status"));
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package com.ga.api.controller.org;
|
||||||
|
|
||||||
|
import com.ga.api.service.org.AgentService;
|
||||||
|
import com.ga.common.annotation.DataChangeLog;
|
||||||
|
import com.ga.common.annotation.RequirePermission;
|
||||||
|
import com.ga.common.excel.ExcelService;
|
||||||
|
import com.ga.common.model.ApiResponse;
|
||||||
|
import com.ga.common.model.PageResponse;
|
||||||
|
import com.ga.core.mapper.org.AgentMapper;
|
||||||
|
import com.ga.core.vo.org.*;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Tag(name = "설계사 관리")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/agents")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AgentController {
|
||||||
|
|
||||||
|
private final AgentService agentService;
|
||||||
|
private final AgentMapper agentMapper;
|
||||||
|
private final ExcelService excelService;
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
@RequirePermission(menu = "ORG_AGENT", perm = "READ")
|
||||||
|
public ApiResponse<PageResponse<AgentResp>> list(@ModelAttribute AgentSearchParam param) {
|
||||||
|
return ApiResponse.ok(agentService.list(param));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{agentId}")
|
||||||
|
@RequirePermission(menu = "ORG_AGENT", perm = "READ")
|
||||||
|
public ApiResponse<AgentResp> detail(@PathVariable Long agentId) {
|
||||||
|
return ApiResponse.ok(agentService.detail(agentId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
@RequirePermission(menu = "ORG_AGENT", perm = "CREATE")
|
||||||
|
@DataChangeLog(menu = "ORG_AGENT", table = "agent")
|
||||||
|
public ApiResponse<Long> create(@Valid @RequestBody AgentSaveReq req) {
|
||||||
|
return ApiResponse.ok(agentService.create(req));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{agentId}")
|
||||||
|
@RequirePermission(menu = "ORG_AGENT", perm = "UPDATE")
|
||||||
|
@DataChangeLog(menu = "ORG_AGENT", table = "agent")
|
||||||
|
public ApiResponse<Void> update(@PathVariable Long agentId, @Valid @RequestBody AgentSaveReq req) {
|
||||||
|
agentService.update(agentId, req);
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{agentId}/status")
|
||||||
|
@RequirePermission(menu = "ORG_AGENT", perm = "UPDATE")
|
||||||
|
@DataChangeLog(menu = "ORG_AGENT", table = "agent")
|
||||||
|
public ApiResponse<Void> changeStatus(@PathVariable Long agentId, @RequestBody Map<String, String> body) {
|
||||||
|
agentService.changeStatus(agentId, body.get("status"));
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{agentId}/history")
|
||||||
|
@RequirePermission(menu = "ORG_AGENT", perm = "READ")
|
||||||
|
public ApiResponse<List<AgentOrgHistoryVO>> history(@PathVariable Long agentId) {
|
||||||
|
return ApiResponse.ok(agentService.history(agentId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/export")
|
||||||
|
@RequirePermission(menu = "ORG_AGENT", perm = "EXPORT")
|
||||||
|
public void export(@ModelAttribute AgentSearchParam param, HttpServletResponse response) {
|
||||||
|
excelService.exportLargeExcel("설계사목록", AgentExcelVO.class,
|
||||||
|
() -> agentMapper.selectCursorForExcel(param), response);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package com.ga.api.controller.org;
|
||||||
|
|
||||||
|
import com.ga.api.service.org.OrganizationService;
|
||||||
|
import com.ga.common.annotation.DataChangeLog;
|
||||||
|
import com.ga.common.annotation.RequirePermission;
|
||||||
|
import com.ga.common.model.ApiResponse;
|
||||||
|
import com.ga.core.vo.org.OrganizationResp;
|
||||||
|
import com.ga.core.vo.org.OrganizationVO;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Tag(name = "조직 관리")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/orgs")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class OrganizationController {
|
||||||
|
|
||||||
|
private final OrganizationService service;
|
||||||
|
|
||||||
|
@GetMapping("/tree")
|
||||||
|
@RequirePermission(menu = "ORG_TREE", perm = "READ")
|
||||||
|
public ApiResponse<List<OrganizationResp>> tree() {
|
||||||
|
return ApiResponse.ok(service.getTree());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
@RequirePermission(menu = "ORG_TREE", perm = "READ")
|
||||||
|
public ApiResponse<List<OrganizationResp>> list(@RequestParam(required = false) String keyword,
|
||||||
|
@RequestParam(required = false) String orgType,
|
||||||
|
@RequestParam(required = false) String isActive) {
|
||||||
|
return ApiResponse.ok(service.list(keyword, orgType, isActive));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{orgId}")
|
||||||
|
@RequirePermission(menu = "ORG_TREE", perm = "READ")
|
||||||
|
public ApiResponse<OrganizationVO> detail(@PathVariable Long orgId) {
|
||||||
|
return ApiResponse.ok(service.get(orgId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
@RequirePermission(menu = "ORG_TREE", perm = "CREATE")
|
||||||
|
@DataChangeLog(menu = "ORG_TREE", table = "organization")
|
||||||
|
public ApiResponse<Long> create(@RequestBody OrganizationVO vo) {
|
||||||
|
return ApiResponse.ok(service.create(vo));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{orgId}")
|
||||||
|
@RequirePermission(menu = "ORG_TREE", perm = "UPDATE")
|
||||||
|
@DataChangeLog(menu = "ORG_TREE", table = "organization")
|
||||||
|
public ApiResponse<Void> update(@PathVariable Long orgId, @RequestBody OrganizationVO vo) {
|
||||||
|
service.update(orgId, vo);
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{orgId}")
|
||||||
|
@RequirePermission(menu = "ORG_TREE", perm = "DELETE")
|
||||||
|
@DataChangeLog(menu = "ORG_TREE", table = "organization")
|
||||||
|
public ApiResponse<Void> delete(@PathVariable Long orgId) {
|
||||||
|
service.delete(orgId);
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package com.ga.api.controller.product;
|
||||||
|
|
||||||
|
import com.ga.common.annotation.DataChangeLog;
|
||||||
|
import com.ga.common.annotation.RequirePermission;
|
||||||
|
import com.ga.common.model.ApiResponse;
|
||||||
|
import com.ga.core.mapper.product.InsuranceCompanyMapper;
|
||||||
|
import com.ga.core.vo.product.InsuranceCompanyVO;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Tag(name = "보험사 관리")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/companies")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CompanyController {
|
||||||
|
|
||||||
|
private final InsuranceCompanyMapper mapper;
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
@RequirePermission(menu = "COMPANY", perm = "READ")
|
||||||
|
public ApiResponse<List<InsuranceCompanyVO>> list(@RequestParam(required = false, defaultValue = "false") boolean activeOnly) {
|
||||||
|
return ApiResponse.ok(activeOnly ? mapper.selectActive() : mapper.selectAll());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{companyId}")
|
||||||
|
@RequirePermission(menu = "COMPANY", perm = "READ")
|
||||||
|
public ApiResponse<InsuranceCompanyVO> detail(@PathVariable Long companyId) {
|
||||||
|
return ApiResponse.ok(mapper.selectById(companyId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
@RequirePermission(menu = "COMPANY", perm = "CREATE")
|
||||||
|
@DataChangeLog(menu = "COMPANY", table = "insurance_company")
|
||||||
|
public ApiResponse<Long> create(@RequestBody InsuranceCompanyVO vo) {
|
||||||
|
mapper.insert(vo);
|
||||||
|
return ApiResponse.ok(vo.getCompanyId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{companyId}")
|
||||||
|
@RequirePermission(menu = "COMPANY", perm = "UPDATE")
|
||||||
|
@DataChangeLog(menu = "COMPANY", table = "insurance_company")
|
||||||
|
public ApiResponse<Void> update(@PathVariable Long companyId, @RequestBody InsuranceCompanyVO vo) {
|
||||||
|
vo.setCompanyId(companyId);
|
||||||
|
mapper.update(vo);
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package com.ga.api.controller.product;
|
||||||
|
|
||||||
|
import com.ga.api.service.product.ContractService;
|
||||||
|
import com.ga.common.annotation.DataChangeLog;
|
||||||
|
import com.ga.common.annotation.RequirePermission;
|
||||||
|
import com.ga.common.model.ApiResponse;
|
||||||
|
import com.ga.common.model.PageResponse;
|
||||||
|
import com.ga.core.vo.product.ContractResp;
|
||||||
|
import com.ga.core.vo.product.ContractSaveReq;
|
||||||
|
import com.ga.core.vo.product.ContractSearchParam;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Tag(name = "계약 관리")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/contracts")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ContractController {
|
||||||
|
|
||||||
|
private final ContractService service;
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
@RequirePermission(menu = "CONTRACT_LIST", perm = "READ")
|
||||||
|
public ApiResponse<PageResponse<ContractResp>> list(@ModelAttribute ContractSearchParam param) {
|
||||||
|
return ApiResponse.ok(service.list(param));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{contractId}")
|
||||||
|
@RequirePermission(menu = "CONTRACT_LIST", perm = "READ")
|
||||||
|
public ApiResponse<ContractResp> detail(@PathVariable Long contractId) {
|
||||||
|
return ApiResponse.ok(service.detail(contractId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
@RequirePermission(menu = "CONTRACT_LIST", perm = "CREATE")
|
||||||
|
@DataChangeLog(menu = "CONTRACT_LIST", table = "contract")
|
||||||
|
public ApiResponse<Long> create(@Valid @RequestBody ContractSaveReq req) {
|
||||||
|
return ApiResponse.ok(service.create(req));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{contractId}")
|
||||||
|
@RequirePermission(menu = "CONTRACT_LIST", perm = "UPDATE")
|
||||||
|
@DataChangeLog(menu = "CONTRACT_LIST", table = "contract")
|
||||||
|
public ApiResponse<Void> update(@PathVariable Long contractId, @Valid @RequestBody ContractSaveReq req) {
|
||||||
|
service.update(contractId, req);
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{contractId}/status")
|
||||||
|
@RequirePermission(menu = "CONTRACT_LIST", perm = "UPDATE")
|
||||||
|
@DataChangeLog(menu = "CONTRACT_LIST", table = "contract")
|
||||||
|
public ApiResponse<Void> changeStatus(@PathVariable Long contractId, @RequestBody Map<String, String> body) {
|
||||||
|
service.changeStatus(contractId, body.get("status"));
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package com.ga.api.controller.product;
|
||||||
|
|
||||||
|
import com.ga.common.annotation.DataChangeLog;
|
||||||
|
import com.ga.common.annotation.RequirePermission;
|
||||||
|
import com.ga.common.exception.BizException;
|
||||||
|
import com.ga.common.exception.ErrorCode;
|
||||||
|
import com.ga.common.model.ApiResponse;
|
||||||
|
import com.ga.core.mapper.product.ProductMapper;
|
||||||
|
import com.ga.core.vo.product.ProductResp;
|
||||||
|
import com.ga.core.vo.product.ProductVO;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Tag(name = "상품 관리")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/products")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ProductController {
|
||||||
|
|
||||||
|
private final ProductMapper mapper;
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
@RequirePermission(menu = "PRODUCT", perm = "READ")
|
||||||
|
public ApiResponse<List<ProductResp>> list(@RequestParam(required = false) Long companyId,
|
||||||
|
@RequestParam(required = false) String insuranceType,
|
||||||
|
@RequestParam(required = false) String keyword,
|
||||||
|
@RequestParam(required = false) String isActive) {
|
||||||
|
return ApiResponse.ok(mapper.selectList(companyId, insuranceType, keyword, isActive));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{productId}")
|
||||||
|
@RequirePermission(menu = "PRODUCT", perm = "READ")
|
||||||
|
public ApiResponse<ProductResp> detail(@PathVariable Long productId) {
|
||||||
|
ProductResp r = mapper.selectDetailById(productId);
|
||||||
|
if (r == null) throw new BizException(ErrorCode.NOT_FOUND);
|
||||||
|
return ApiResponse.ok(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
@RequirePermission(menu = "PRODUCT", perm = "CREATE")
|
||||||
|
@DataChangeLog(menu = "PRODUCT", table = "product")
|
||||||
|
public ApiResponse<Long> create(@RequestBody ProductVO vo) {
|
||||||
|
if (mapper.existsByCode(vo.getCompanyId(), vo.getProductCode()) > 0) {
|
||||||
|
throw new BizException(ErrorCode.DUPLICATE_DATA);
|
||||||
|
}
|
||||||
|
mapper.insert(vo);
|
||||||
|
return ApiResponse.ok(vo.getProductId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{productId}")
|
||||||
|
@RequirePermission(menu = "PRODUCT", perm = "UPDATE")
|
||||||
|
@DataChangeLog(menu = "PRODUCT", table = "product")
|
||||||
|
public ApiResponse<Void> update(@PathVariable Long productId, @RequestBody ProductVO vo) {
|
||||||
|
vo.setProductId(productId);
|
||||||
|
mapper.update(vo);
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{productId}")
|
||||||
|
@RequirePermission(menu = "PRODUCT", perm = "DELETE")
|
||||||
|
public ApiResponse<Void> delete(@PathVariable Long productId) {
|
||||||
|
mapper.deleteById(productId);
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
package com.ga.api.controller.receive;
|
||||||
|
|
||||||
|
import com.ga.common.annotation.RequirePermission;
|
||||||
|
import com.ga.common.model.ApiResponse;
|
||||||
|
import com.ga.core.mapper.receive.ReceiveMapper;
|
||||||
|
import com.ga.core.vo.receive.*;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Tag(name = "데이터 수신")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/receive")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ReceiveController {
|
||||||
|
|
||||||
|
private final ReceiveMapper mapper;
|
||||||
|
|
||||||
|
// 보험사 프로파일
|
||||||
|
@GetMapping("/profiles")
|
||||||
|
@RequirePermission(menu = "RECEIVE_MAPPING", perm = "READ")
|
||||||
|
public ApiResponse<List<CompanyProfileVO>> profiles() {
|
||||||
|
return ApiResponse.ok(mapper.selectAllProfiles());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/profiles/{companyCode}")
|
||||||
|
@RequirePermission(menu = "RECEIVE_MAPPING", perm = "UPDATE")
|
||||||
|
public ApiResponse<Void> updateProfile(@PathVariable String companyCode, @RequestBody CompanyProfileVO vo) {
|
||||||
|
vo.setCompanyCode(companyCode);
|
||||||
|
if (mapper.selectProfile(companyCode) == null) mapper.insertProfile(vo);
|
||||||
|
else mapper.updateProfile(vo);
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 필드 매핑
|
||||||
|
@GetMapping("/mapping/{companyCode}/fields")
|
||||||
|
@RequirePermission(menu = "RECEIVE_MAPPING", perm = "READ")
|
||||||
|
public ApiResponse<List<CompanyFieldMappingVO>> fields(@PathVariable String companyCode,
|
||||||
|
@RequestParam(required = false) String targetTable) {
|
||||||
|
return ApiResponse.ok(mapper.selectFieldMappings(companyCode, targetTable));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/mapping/{companyCode}/fields")
|
||||||
|
@RequirePermission(menu = "RECEIVE_MAPPING", perm = "CREATE")
|
||||||
|
public ApiResponse<Long> createField(@PathVariable String companyCode, @RequestBody CompanyFieldMappingVO vo) {
|
||||||
|
vo.setCompanyCode(companyCode);
|
||||||
|
mapper.insertFieldMapping(vo);
|
||||||
|
return ApiResponse.ok(vo.getMappingId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/mapping/fields/{mappingId}")
|
||||||
|
@RequirePermission(menu = "RECEIVE_MAPPING", perm = "UPDATE")
|
||||||
|
public ApiResponse<Void> updateField(@PathVariable Long mappingId, @RequestBody CompanyFieldMappingVO vo) {
|
||||||
|
vo.setMappingId(mappingId);
|
||||||
|
mapper.updateFieldMapping(vo);
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/mapping/fields/{mappingId}")
|
||||||
|
@RequirePermission(menu = "RECEIVE_MAPPING", perm = "DELETE")
|
||||||
|
public ApiResponse<Void> deleteField(@PathVariable Long mappingId) {
|
||||||
|
mapper.deleteFieldMapping(mappingId);
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 코드 매핑
|
||||||
|
@GetMapping("/mapping/{companyCode}/codes")
|
||||||
|
@RequirePermission(menu = "RECEIVE_MAPPING", perm = "READ")
|
||||||
|
public ApiResponse<List<CodeMappingVO>> codes(@PathVariable String companyCode,
|
||||||
|
@RequestParam(required = false) String codeType) {
|
||||||
|
return ApiResponse.ok(mapper.selectCodeMappings(companyCode, codeType));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/mapping/{companyCode}/codes")
|
||||||
|
@RequirePermission(menu = "RECEIVE_MAPPING", perm = "CREATE")
|
||||||
|
public ApiResponse<Long> createCode(@PathVariable String companyCode, @RequestBody CodeMappingVO vo) {
|
||||||
|
vo.setCompanyCode(companyCode);
|
||||||
|
mapper.insertCodeMapping(vo);
|
||||||
|
return ApiResponse.ok(vo.getCodeId());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 원본 데이터 + 에러
|
||||||
|
@GetMapping("/raw")
|
||||||
|
@RequirePermission(menu = "RECEIVE_DATA", perm = "READ")
|
||||||
|
public ApiResponse<List<RawCommissionDataVO>> rawList(@RequestParam(required = false) String companyCode,
|
||||||
|
@RequestParam(required = false) String settleMonth,
|
||||||
|
@RequestParam(required = false) String parseStatus) {
|
||||||
|
return ApiResponse.ok(mapper.selectRawList(companyCode, settleMonth, parseStatus));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/errors")
|
||||||
|
@RequirePermission(menu = "RECEIVE_DATA", perm = "READ")
|
||||||
|
public ApiResponse<List<ParseErrorLogVO>> errors(@RequestParam(required = false) String companyCode,
|
||||||
|
@RequestParam(required = false) String resolveStatus) {
|
||||||
|
return ApiResponse.ok(mapper.selectErrors(companyCode, resolveStatus));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/errors/{errorId}/resolve")
|
||||||
|
@RequirePermission(menu = "RECEIVE_DATA", perm = "UPDATE")
|
||||||
|
public ApiResponse<Void> resolveError(@PathVariable Long errorId, @RequestBody Map<String, String> body) {
|
||||||
|
mapper.resolveError(errorId,
|
||||||
|
body.getOrDefault("status", "RESOLVED"),
|
||||||
|
body.get("note"),
|
||||||
|
com.ga.common.util.SecurityUtil.getCurrentUserId());
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
package com.ga.api.controller.rule;
|
||||||
|
|
||||||
|
import com.ga.common.annotation.DataChangeLog;
|
||||||
|
import com.ga.common.annotation.RequirePermission;
|
||||||
|
import com.ga.common.model.ApiResponse;
|
||||||
|
import com.ga.core.mapper.rule.RuleMapper;
|
||||||
|
import com.ga.core.vo.rule.*;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Tag(name = "수수료 규정")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/rules")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class RuleController {
|
||||||
|
|
||||||
|
private final RuleMapper mapper;
|
||||||
|
|
||||||
|
// ========== commission_rate ==========
|
||||||
|
@GetMapping("/commission-rates")
|
||||||
|
@RequirePermission(menu = "RULE_COMMISSION", perm = "READ")
|
||||||
|
public ApiResponse<List<CommissionRateVO>> listCommissionRates(@RequestParam(required = false) Long productId,
|
||||||
|
@RequestParam(required = false) Integer year) {
|
||||||
|
return ApiResponse.ok(mapper.selectCommissionRates(productId, year));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/commission-rates")
|
||||||
|
@RequirePermission(menu = "RULE_COMMISSION", perm = "CREATE")
|
||||||
|
@DataChangeLog(menu = "RULE_COMMISSION", table = "commission_rate")
|
||||||
|
public ApiResponse<Long> createCommissionRate(@RequestBody CommissionRateVO vo) {
|
||||||
|
mapper.insertCommissionRate(vo);
|
||||||
|
return ApiResponse.ok(vo.getRateId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/commission-rates/{rateId}")
|
||||||
|
@RequirePermission(menu = "RULE_COMMISSION", perm = "UPDATE")
|
||||||
|
@DataChangeLog(menu = "RULE_COMMISSION", table = "commission_rate")
|
||||||
|
public ApiResponse<Void> updateCommissionRate(@PathVariable Long rateId, @RequestBody CommissionRateVO vo) {
|
||||||
|
vo.setRateId(rateId);
|
||||||
|
mapper.updateCommissionRate(vo);
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/commission-rates/{rateId}")
|
||||||
|
@RequirePermission(menu = "RULE_COMMISSION", perm = "DELETE")
|
||||||
|
public ApiResponse<Void> deleteCommissionRate(@PathVariable Long rateId) {
|
||||||
|
mapper.deleteCommissionRate(rateId);
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== payout_rule ==========
|
||||||
|
@GetMapping("/payout")
|
||||||
|
@RequirePermission(menu = "RULE_PAYOUT", perm = "READ")
|
||||||
|
public ApiResponse<List<PayoutRuleVO>> listPayoutRules(@RequestParam(required = false) Long gradeId,
|
||||||
|
@RequestParam(required = false) String insuranceType) {
|
||||||
|
return ApiResponse.ok(mapper.selectPayoutRules(gradeId, insuranceType));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/payout")
|
||||||
|
@RequirePermission(menu = "RULE_PAYOUT", perm = "CREATE")
|
||||||
|
@DataChangeLog(menu = "RULE_PAYOUT", table = "payout_rule")
|
||||||
|
public ApiResponse<Long> createPayoutRule(@RequestBody PayoutRuleVO vo) {
|
||||||
|
mapper.insertPayoutRule(vo);
|
||||||
|
return ApiResponse.ok(vo.getRuleId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/payout/{ruleId}")
|
||||||
|
@RequirePermission(menu = "RULE_PAYOUT", perm = "UPDATE")
|
||||||
|
@DataChangeLog(menu = "RULE_PAYOUT", table = "payout_rule")
|
||||||
|
public ApiResponse<Void> updatePayoutRule(@PathVariable Long ruleId, @RequestBody PayoutRuleVO vo) {
|
||||||
|
vo.setRuleId(ruleId);
|
||||||
|
mapper.updatePayoutRule(vo);
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/payout/{ruleId}")
|
||||||
|
@RequirePermission(menu = "RULE_PAYOUT", perm = "DELETE")
|
||||||
|
public ApiResponse<Void> deletePayoutRule(@PathVariable Long ruleId) {
|
||||||
|
mapper.deletePayoutRule(ruleId);
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== override_rule ==========
|
||||||
|
@GetMapping("/override")
|
||||||
|
@RequirePermission(menu = "RULE_OVERRIDE", perm = "READ")
|
||||||
|
public ApiResponse<List<OverrideRuleVO>> listOverrideRules() {
|
||||||
|
return ApiResponse.ok(mapper.selectOverrideRules());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/override")
|
||||||
|
@RequirePermission(menu = "RULE_OVERRIDE", perm = "CREATE")
|
||||||
|
public ApiResponse<Long> createOverrideRule(@RequestBody OverrideRuleVO vo) {
|
||||||
|
mapper.insertOverrideRule(vo);
|
||||||
|
return ApiResponse.ok(vo.getOverrideId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/override/{overrideId}")
|
||||||
|
@RequirePermission(menu = "RULE_OVERRIDE", perm = "UPDATE")
|
||||||
|
public ApiResponse<Void> updateOverrideRule(@PathVariable Long overrideId, @RequestBody OverrideRuleVO vo) {
|
||||||
|
vo.setOverrideId(overrideId);
|
||||||
|
mapper.updateOverrideRule(vo);
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/override/{overrideId}")
|
||||||
|
@RequirePermission(menu = "RULE_OVERRIDE", perm = "DELETE")
|
||||||
|
public ApiResponse<Void> deleteOverrideRule(@PathVariable Long overrideId) {
|
||||||
|
mapper.deleteOverrideRule(overrideId);
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== chargeback_rule ==========
|
||||||
|
@GetMapping("/chargeback")
|
||||||
|
@RequirePermission(menu = "RULE_CHARGEBACK", perm = "READ")
|
||||||
|
public ApiResponse<List<ChargebackRuleVO>> listChargebackRules(@RequestParam(required = false) String companyCode) {
|
||||||
|
return ApiResponse.ok(mapper.selectChargebackRules(companyCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/chargeback")
|
||||||
|
@RequirePermission(menu = "RULE_CHARGEBACK", perm = "CREATE")
|
||||||
|
public ApiResponse<Long> createChargebackRule(@RequestBody ChargebackRuleVO vo) {
|
||||||
|
mapper.insertChargebackRule(vo);
|
||||||
|
return ApiResponse.ok(vo.getCbRuleId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/chargeback/{cbRuleId}")
|
||||||
|
@RequirePermission(menu = "RULE_CHARGEBACK", perm = "UPDATE")
|
||||||
|
public ApiResponse<Void> updateChargebackRule(@PathVariable Long cbRuleId, @RequestBody ChargebackRuleVO vo) {
|
||||||
|
vo.setCbRuleId(cbRuleId);
|
||||||
|
mapper.updateChargebackRule(vo);
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/chargeback/{cbRuleId}")
|
||||||
|
@RequirePermission(menu = "RULE_CHARGEBACK", perm = "DELETE")
|
||||||
|
public ApiResponse<Void> deleteChargebackRule(@PathVariable Long cbRuleId) {
|
||||||
|
mapper.deleteChargebackRule(cbRuleId);
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== exception_type_code ==========
|
||||||
|
@GetMapping("/exception-codes")
|
||||||
|
@RequirePermission(menu = "RULE_EXCEPTION", perm = "READ")
|
||||||
|
public ApiResponse<List<ExceptionTypeCodeVO>> listExceptionCodes() {
|
||||||
|
return ApiResponse.ok(mapper.selectExceptionCodes());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/exception-codes")
|
||||||
|
@RequirePermission(menu = "RULE_EXCEPTION", perm = "CREATE")
|
||||||
|
public ApiResponse<Void> createExceptionCode(@RequestBody ExceptionTypeCodeVO vo) {
|
||||||
|
mapper.insertExceptionCode(vo);
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/exception-codes/{exceptionCode}")
|
||||||
|
@RequirePermission(menu = "RULE_EXCEPTION", perm = "UPDATE")
|
||||||
|
public ApiResponse<Void> updateExceptionCode(@PathVariable String exceptionCode,
|
||||||
|
@RequestBody ExceptionTypeCodeVO vo) {
|
||||||
|
vo.setExceptionCode(exceptionCode);
|
||||||
|
mapper.updateExceptionCode(vo);
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package com.ga.api.controller.settle;
|
||||||
|
|
||||||
|
import com.ga.common.annotation.DataChangeLog;
|
||||||
|
import com.ga.common.annotation.RequirePermission;
|
||||||
|
import com.ga.common.model.ApiResponse;
|
||||||
|
import com.ga.common.model.PageResponse;
|
||||||
|
import com.ga.core.mapper.settle.PaymentMapper;
|
||||||
|
import com.ga.core.vo.settle.PaymentResp;
|
||||||
|
import com.ga.core.vo.settle.SettleSearchParam;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Tag(name = "지급 관리")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/payments")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class PaymentController {
|
||||||
|
|
||||||
|
private final PaymentMapper mapper;
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
@RequirePermission(menu = "PAYMENT", perm = "READ")
|
||||||
|
public ApiResponse<PageResponse<PaymentResp>> list(@ModelAttribute SettleSearchParam param) {
|
||||||
|
param.startPage();
|
||||||
|
return ApiResponse.ok(PageResponse.of(mapper.selectList(param)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{paymentId}/status")
|
||||||
|
@RequirePermission(menu = "PAYMENT", perm = "UPDATE")
|
||||||
|
@DataChangeLog(menu = "PAYMENT", table = "payment")
|
||||||
|
public ApiResponse<Void> updateStatus(@PathVariable Long paymentId, @RequestBody Map<String, String> body) {
|
||||||
|
mapper.updateStatus(paymentId, body.get("status"), body.get("failReason"));
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package com.ga.api.controller.settle;
|
||||||
|
|
||||||
|
import com.ga.api.service.settle.SettleService;
|
||||||
|
import com.ga.common.annotation.DataChangeLog;
|
||||||
|
import com.ga.common.annotation.RequirePermission;
|
||||||
|
import com.ga.common.model.ApiResponse;
|
||||||
|
import com.ga.common.model.PageResponse;
|
||||||
|
import com.ga.core.vo.settle.SettleMasterResp;
|
||||||
|
import com.ga.core.vo.settle.SettleSearchParam;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Tag(name = "정산 관리")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/settle")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SettleController {
|
||||||
|
|
||||||
|
private final SettleService service;
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
@RequirePermission(menu = "SETTLE_LIST", perm = "READ")
|
||||||
|
public ApiResponse<PageResponse<SettleMasterResp>> list(@ModelAttribute SettleSearchParam param) {
|
||||||
|
return ApiResponse.ok(service.list(param));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{agentId}/months/{settleMonth}")
|
||||||
|
@RequirePermission(menu = "SETTLE_LIST", perm = "READ")
|
||||||
|
public ApiResponse<SettleMasterResp> detail(@PathVariable Long agentId,
|
||||||
|
@PathVariable String settleMonth) {
|
||||||
|
return ApiResponse.ok(service.detail(agentId, settleMonth));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/summary")
|
||||||
|
@RequirePermission(menu = "SETTLE_LIST", perm = "READ")
|
||||||
|
public ApiResponse<Map<String, Object>> summary(@RequestParam String settleMonth) {
|
||||||
|
return ApiResponse.ok(service.summary(settleMonth));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/org-summary")
|
||||||
|
@RequirePermission(menu = "SETTLE_LIST", perm = "READ")
|
||||||
|
public ApiResponse<List<Map<String, Object>>> orgSummary(@RequestParam String settleMonth) {
|
||||||
|
return ApiResponse.ok(service.orgSummary(settleMonth));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{settleId}/confirm")
|
||||||
|
@RequirePermission(menu = "SETTLE_LIST", perm = "APPROVE")
|
||||||
|
@DataChangeLog(menu = "SETTLE_LIST", table = "settle_master")
|
||||||
|
public ApiResponse<Void> confirm(@PathVariable Long settleId) {
|
||||||
|
service.confirm(settleId);
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{settleId}/hold")
|
||||||
|
@RequirePermission(menu = "SETTLE_LIST", perm = "APPROVE")
|
||||||
|
@DataChangeLog(menu = "SETTLE_LIST", table = "settle_master")
|
||||||
|
public ApiResponse<Void> hold(@PathVariable Long settleId, @RequestBody Map<String, String> body) {
|
||||||
|
service.hold(settleId, body.get("reason"));
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{settleId}/release")
|
||||||
|
@RequirePermission(menu = "SETTLE_LIST", perm = "APPROVE")
|
||||||
|
@DataChangeLog(menu = "SETTLE_LIST", table = "settle_master")
|
||||||
|
public ApiResponse<Void> release(@PathVariable Long settleId) {
|
||||||
|
service.release(settleId);
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package com.ga.api.controller.system;
|
||||||
|
|
||||||
|
import com.ga.common.annotation.DataChangeLog;
|
||||||
|
import com.ga.common.annotation.RequirePermission;
|
||||||
|
import com.ga.common.model.ApiResponse;
|
||||||
|
import com.ga.core.mapper.user.RoleMapper;
|
||||||
|
import com.ga.core.vo.user.RoleVO;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Tag(name = "역할 관리")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/system/roles")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class RoleController {
|
||||||
|
|
||||||
|
private final RoleMapper mapper;
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
@RequirePermission(menu = "SYSTEM_ROLE", perm = "READ")
|
||||||
|
public ApiResponse<List<RoleVO>> list() {
|
||||||
|
return ApiResponse.ok(mapper.selectAll());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{roleId}")
|
||||||
|
@RequirePermission(menu = "SYSTEM_ROLE", perm = "READ")
|
||||||
|
public ApiResponse<RoleVO> detail(@PathVariable Long roleId) {
|
||||||
|
return ApiResponse.ok(mapper.selectById(roleId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{roleId}/permissions")
|
||||||
|
@RequirePermission(menu = "SYSTEM_ROLE", perm = "READ")
|
||||||
|
public ApiResponse<List<Map<String, Object>>> permissions(@PathVariable Long roleId) {
|
||||||
|
return ApiResponse.ok(mapper.selectRolePermissions(roleId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
@RequirePermission(menu = "SYSTEM_ROLE", perm = "CREATE")
|
||||||
|
@DataChangeLog(menu = "SYSTEM_ROLE", table = "role")
|
||||||
|
public ApiResponse<Long> create(@RequestBody RoleVO vo) {
|
||||||
|
mapper.insert(vo);
|
||||||
|
return ApiResponse.ok(vo.getRoleId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{roleId}")
|
||||||
|
@RequirePermission(menu = "SYSTEM_ROLE", perm = "UPDATE")
|
||||||
|
@DataChangeLog(menu = "SYSTEM_ROLE", table = "role")
|
||||||
|
public ApiResponse<Void> update(@PathVariable Long roleId, @RequestBody RoleVO vo) {
|
||||||
|
vo.setRoleId(roleId);
|
||||||
|
mapper.update(vo);
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{roleId}")
|
||||||
|
@RequirePermission(menu = "SYSTEM_ROLE", perm = "DELETE")
|
||||||
|
public ApiResponse<Void> delete(@PathVariable Long roleId) {
|
||||||
|
mapper.deleteById(roleId);
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 권한 매트릭스 일괄 저장 */
|
||||||
|
@PutMapping("/{roleId}/permissions")
|
||||||
|
@RequirePermission(menu = "SYSTEM_ROLE", perm = "UPDATE")
|
||||||
|
@Transactional
|
||||||
|
public ApiResponse<Void> savePermissions(@PathVariable Long roleId,
|
||||||
|
@RequestBody List<Map<String, Object>> permissions) {
|
||||||
|
mapper.deleteRolePermissions(roleId);
|
||||||
|
for (Map<String, Object> p : permissions) {
|
||||||
|
Long menuId = ((Number) p.get("menuId")).longValue();
|
||||||
|
String permCode = (String) p.get("permCode");
|
||||||
|
String isGranted = (String) p.getOrDefault("isGranted", "Y");
|
||||||
|
mapper.insertRolePermission(roleId, menuId, permCode, isGranted);
|
||||||
|
}
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package com.ga.api.controller.system;
|
||||||
|
|
||||||
|
import com.ga.api.service.system.UserService;
|
||||||
|
import com.ga.common.annotation.DataChangeLog;
|
||||||
|
import com.ga.common.annotation.RequirePermission;
|
||||||
|
import com.ga.common.model.ApiResponse;
|
||||||
|
import com.ga.common.model.PageResponse;
|
||||||
|
import com.ga.core.vo.user.UserResp;
|
||||||
|
import com.ga.core.vo.user.UserSaveReq;
|
||||||
|
import com.ga.core.vo.user.UserSearchParam;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@Tag(name = "사용자 관리")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/system/users")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class UserController {
|
||||||
|
|
||||||
|
private final UserService service;
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
@RequirePermission(menu = "SYSTEM_USER", perm = "READ")
|
||||||
|
public ApiResponse<PageResponse<UserResp>> list(@ModelAttribute UserSearchParam param) {
|
||||||
|
return ApiResponse.ok(service.list(param));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{userId}")
|
||||||
|
@RequirePermission(menu = "SYSTEM_USER", perm = "READ")
|
||||||
|
public ApiResponse<UserResp> detail(@PathVariable Long userId) {
|
||||||
|
return ApiResponse.ok(service.detail(userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
@RequirePermission(menu = "SYSTEM_USER", perm = "CREATE")
|
||||||
|
@DataChangeLog(menu = "SYSTEM_USER", table = "users")
|
||||||
|
public ApiResponse<Long> create(@Valid @RequestBody UserSaveReq req) {
|
||||||
|
return ApiResponse.ok(service.create(req));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{userId}")
|
||||||
|
@RequirePermission(menu = "SYSTEM_USER", perm = "UPDATE")
|
||||||
|
@DataChangeLog(menu = "SYSTEM_USER", table = "users")
|
||||||
|
public ApiResponse<Void> update(@PathVariable Long userId, @Valid @RequestBody UserSaveReq req) {
|
||||||
|
service.update(userId, req);
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{userId}/reset-password")
|
||||||
|
@RequirePermission(menu = "SYSTEM_USER", perm = "UPDATE")
|
||||||
|
public ApiResponse<Void> resetPassword(@PathVariable Long userId) {
|
||||||
|
service.resetPassword(userId);
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{userId}/unlock")
|
||||||
|
@RequirePermission(menu = "SYSTEM_USER", perm = "UPDATE")
|
||||||
|
public ApiResponse<Void> unlock(@PathVariable Long userId) {
|
||||||
|
service.unlock(userId);
|
||||||
|
return ApiResponse.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package com.ga.api.service.ledger;
|
||||||
|
|
||||||
|
import com.ga.common.exception.BizException;
|
||||||
|
import com.ga.common.exception.ErrorCode;
|
||||||
|
import com.ga.common.model.PageResponse;
|
||||||
|
import com.ga.common.util.SecurityUtil;
|
||||||
|
import com.ga.core.mapper.ledger.ExceptionLedgerMapper;
|
||||||
|
import com.ga.core.mapper.ledger.MaintainLedgerMapper;
|
||||||
|
import com.ga.core.mapper.ledger.RecruitLedgerMapper;
|
||||||
|
import com.ga.core.vo.ledger.*;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class LedgerService {
|
||||||
|
|
||||||
|
private final RecruitLedgerMapper recruitMapper;
|
||||||
|
private final MaintainLedgerMapper maintainMapper;
|
||||||
|
private final ExceptionLedgerMapper exceptionMapper;
|
||||||
|
|
||||||
|
public PageResponse<LedgerResp> listRecruit(LedgerSearchParam param) {
|
||||||
|
param.startPage();
|
||||||
|
return PageResponse.of(recruitMapper.selectList(param));
|
||||||
|
}
|
||||||
|
|
||||||
|
public PageResponse<LedgerResp> listMaintain(LedgerSearchParam param) {
|
||||||
|
param.startPage();
|
||||||
|
return PageResponse.of(maintainMapper.selectList(param));
|
||||||
|
}
|
||||||
|
|
||||||
|
public PageResponse<ExceptionLedgerResp> listException(LedgerSearchParam param) {
|
||||||
|
param.startPage();
|
||||||
|
return PageResponse.of(exceptionMapper.selectList(param));
|
||||||
|
}
|
||||||
|
|
||||||
|
public LedgerResp detailRecruit(Long ledgerId) {
|
||||||
|
LedgerResp r = recruitMapper.selectDetailById(ledgerId);
|
||||||
|
if (r == null) throw new BizException(ErrorCode.NOT_FOUND);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LedgerResp detailMaintain(Long ledgerId) {
|
||||||
|
LedgerResp r = maintainMapper.selectDetailById(ledgerId);
|
||||||
|
if (r == null) throw new BizException(ErrorCode.NOT_FOUND);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExceptionLedgerResp detailException(Long ledgerId) {
|
||||||
|
ExceptionLedgerResp r = exceptionMapper.selectDetailById(ledgerId);
|
||||||
|
if (r == null) throw new BizException(ErrorCode.NOT_FOUND);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Long createException(ExceptionLedgerSaveReq req) {
|
||||||
|
ExceptionLedgerVO vo = new ExceptionLedgerVO();
|
||||||
|
vo.setAgentId(req.getAgentId());
|
||||||
|
vo.setExceptionCode(req.getExceptionCode());
|
||||||
|
vo.setSettleMonth(req.getSettleMonth());
|
||||||
|
vo.setAmount(req.getAmount());
|
||||||
|
vo.setPolicyNo(req.getPolicyNo());
|
||||||
|
vo.setCompanyCode(req.getCompanyCode());
|
||||||
|
vo.setInsuranceType(req.getInsuranceType());
|
||||||
|
vo.setDescription(req.getDescription());
|
||||||
|
vo.setRecurringYn(req.getRecurringYn());
|
||||||
|
vo.setRecurringMonths(req.getRecurringMonths());
|
||||||
|
vo.setRecurringLeft(req.getRecurringMonths());
|
||||||
|
vo.setApproveStatus("PENDING");
|
||||||
|
vo.setStatus("NEW");
|
||||||
|
exceptionMapper.insert(vo);
|
||||||
|
return vo.getLedgerId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void approveException(Long ledgerId, String status) {
|
||||||
|
if (!"APPROVED".equals(status) && !"REJECTED".equals(status)) {
|
||||||
|
throw new BizException(ErrorCode.INVALID_PARAMETER, "status는 APPROVED/REJECTED만 가능합니다");
|
||||||
|
}
|
||||||
|
Long approverId = SecurityUtil.getCurrentUserId();
|
||||||
|
if (approverId == null) throw new BizException(ErrorCode.UNAUTHORIZED);
|
||||||
|
exceptionMapper.updateApprove(ledgerId, status, approverId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
package com.ga.api.service.org;
|
||||||
|
|
||||||
|
import com.ga.common.code.CommonCodeService;
|
||||||
|
import com.ga.common.exception.BizException;
|
||||||
|
import com.ga.common.exception.ErrorCode;
|
||||||
|
import com.ga.common.model.PageResponse;
|
||||||
|
import com.ga.core.mapper.org.AgentMapper;
|
||||||
|
import com.ga.core.vo.org.*;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AgentService {
|
||||||
|
|
||||||
|
private final AgentMapper mapper;
|
||||||
|
private final CommonCodeService codeService;
|
||||||
|
|
||||||
|
public PageResponse<AgentResp> list(AgentSearchParam param) {
|
||||||
|
param.startPage();
|
||||||
|
return PageResponse.of(mapper.selectList(param));
|
||||||
|
}
|
||||||
|
|
||||||
|
public AgentResp detail(Long agentId) {
|
||||||
|
AgentResp resp = mapper.selectDetailById(agentId);
|
||||||
|
if (resp == null) throw new BizException(ErrorCode.NOT_FOUND);
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Long create(AgentSaveReq req) {
|
||||||
|
if (req.getLicenseNo() != null && mapper.existsByLicenseNo(req.getLicenseNo()) > 0) {
|
||||||
|
throw new BizException(ErrorCode.DUPLICATE_DATA, "이미 등록된 사번입니다");
|
||||||
|
}
|
||||||
|
AgentVO vo = toVO(req, null);
|
||||||
|
vo.setStatus("ACTIVE");
|
||||||
|
mapper.insert(vo);
|
||||||
|
// 입사 이력 기록
|
||||||
|
AgentOrgHistoryVO h = new AgentOrgHistoryVO();
|
||||||
|
h.setAgentId(vo.getAgentId());
|
||||||
|
h.setToOrgId(vo.getOrgId());
|
||||||
|
h.setToGradeId(vo.getGradeId());
|
||||||
|
h.setChangeType("JOIN");
|
||||||
|
h.setChangeDate(req.getJoinDate() != null ? req.getJoinDate() : java.time.LocalDate.now());
|
||||||
|
mapper.insertHistory(h);
|
||||||
|
return vo.getAgentId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void update(Long agentId, AgentSaveReq req) {
|
||||||
|
AgentVO old = mapper.selectById(agentId);
|
||||||
|
if (old == null) throw new BizException(ErrorCode.NOT_FOUND);
|
||||||
|
AgentVO vo = toVO(req, agentId);
|
||||||
|
vo.setStatus(old.getStatus());
|
||||||
|
mapper.update(vo);
|
||||||
|
|
||||||
|
// 조직/직급 변경 시 이력
|
||||||
|
if (!java.util.Objects.equals(old.getOrgId(), vo.getOrgId())
|
||||||
|
|| !java.util.Objects.equals(old.getGradeId(), vo.getGradeId())) {
|
||||||
|
AgentOrgHistoryVO h = new AgentOrgHistoryVO();
|
||||||
|
h.setAgentId(agentId);
|
||||||
|
h.setFromOrgId(old.getOrgId());
|
||||||
|
h.setToOrgId(vo.getOrgId());
|
||||||
|
h.setFromGradeId(old.getGradeId());
|
||||||
|
h.setToGradeId(vo.getGradeId());
|
||||||
|
h.setChangeType(!java.util.Objects.equals(old.getGradeId(), vo.getGradeId()) ? "PROMOTE" : "TRANSFER");
|
||||||
|
h.setChangeDate(java.time.LocalDate.now());
|
||||||
|
mapper.insertHistory(h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void changeStatus(Long agentId, String status) {
|
||||||
|
codeService.validateCode("AGENT_STATUS", status);
|
||||||
|
mapper.updateStatus(agentId, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
public java.util.List<AgentOrgHistoryVO> history(Long agentId) {
|
||||||
|
return mapper.selectHistory(agentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AgentVO toVO(AgentSaveReq req, Long agentId) {
|
||||||
|
AgentVO vo = new AgentVO();
|
||||||
|
vo.setAgentId(agentId);
|
||||||
|
vo.setOrgId(req.getOrgId());
|
||||||
|
vo.setGradeId(req.getGradeId());
|
||||||
|
vo.setAgentName(req.getAgentName());
|
||||||
|
vo.setResidentNo(req.getResidentNo());
|
||||||
|
vo.setLicenseNo(req.getLicenseNo());
|
||||||
|
vo.setPhone(req.getPhone());
|
||||||
|
vo.setEmail(req.getEmail());
|
||||||
|
vo.setBankCode(req.getBankCode());
|
||||||
|
vo.setAccountNo(req.getAccountNo());
|
||||||
|
vo.setJoinDate(req.getJoinDate());
|
||||||
|
return vo;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package com.ga.api.service.org;
|
||||||
|
|
||||||
|
import com.ga.common.exception.BizException;
|
||||||
|
import com.ga.common.exception.ErrorCode;
|
||||||
|
import com.ga.core.mapper.org.OrganizationMapper;
|
||||||
|
import com.ga.core.vo.org.OrganizationResp;
|
||||||
|
import com.ga.core.vo.org.OrganizationVO;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class OrganizationService {
|
||||||
|
|
||||||
|
private final OrganizationMapper mapper;
|
||||||
|
|
||||||
|
public List<OrganizationResp> getTree() {
|
||||||
|
List<OrganizationResp> flat = mapper.selectAllForTree();
|
||||||
|
return buildTree(flat);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<OrganizationResp> list(String keyword, String orgType, String isActive) {
|
||||||
|
return mapper.selectList(keyword, orgType, isActive);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OrganizationVO get(Long orgId) {
|
||||||
|
OrganizationVO vo = mapper.selectById(orgId);
|
||||||
|
if (vo == null) throw new BizException(ErrorCode.NOT_FOUND);
|
||||||
|
return vo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Long create(OrganizationVO vo) {
|
||||||
|
mapper.insert(vo);
|
||||||
|
return vo.getOrgId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void update(Long orgId, OrganizationVO vo) {
|
||||||
|
get(orgId);
|
||||||
|
vo.setOrgId(orgId);
|
||||||
|
mapper.update(vo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void delete(Long orgId) {
|
||||||
|
if (mapper.countChildren(orgId) > 0) {
|
||||||
|
throw new BizException(ErrorCode.DEPENDENT_DATA, "하위 조직이 있어 삭제할 수 없습니다");
|
||||||
|
}
|
||||||
|
if (mapper.countAgents(orgId) > 0) {
|
||||||
|
throw new BizException(ErrorCode.DEPENDENT_DATA, "소속 설계사가 있어 삭제할 수 없습니다");
|
||||||
|
}
|
||||||
|
mapper.deleteById(orgId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<OrganizationResp> buildTree(List<OrganizationResp> flat) {
|
||||||
|
Map<Long, OrganizationResp> byId = new LinkedHashMap<>();
|
||||||
|
for (OrganizationResp o : flat) byId.put(o.getOrgId(), o);
|
||||||
|
List<OrganizationResp> roots = new ArrayList<>();
|
||||||
|
for (OrganizationResp o : flat) {
|
||||||
|
if (o.getParentOrgId() == null) {
|
||||||
|
roots.add(o);
|
||||||
|
} else {
|
||||||
|
OrganizationResp parent = byId.get(o.getParentOrgId());
|
||||||
|
if (parent != null) parent.getChildren().add(o);
|
||||||
|
else roots.add(o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return roots;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package com.ga.api.service.product;
|
||||||
|
|
||||||
|
import com.ga.common.exception.BizException;
|
||||||
|
import com.ga.common.exception.ErrorCode;
|
||||||
|
import com.ga.common.model.PageResponse;
|
||||||
|
import com.ga.core.mapper.product.ContractMapper;
|
||||||
|
import com.ga.core.vo.product.ContractResp;
|
||||||
|
import com.ga.core.vo.product.ContractSaveReq;
|
||||||
|
import com.ga.core.vo.product.ContractSearchParam;
|
||||||
|
import com.ga.core.vo.product.ContractVO;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ContractService {
|
||||||
|
|
||||||
|
private final ContractMapper mapper;
|
||||||
|
|
||||||
|
public PageResponse<ContractResp> list(ContractSearchParam param) {
|
||||||
|
param.startPage();
|
||||||
|
return PageResponse.of(mapper.selectList(param));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ContractResp detail(Long contractId) {
|
||||||
|
ContractResp resp = mapper.selectDetailById(contractId);
|
||||||
|
if (resp == null) throw new BizException(ErrorCode.NOT_FOUND);
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Long create(ContractSaveReq req) {
|
||||||
|
if (mapper.existsByPolicyNo(req.getPolicyNo()) > 0) {
|
||||||
|
throw new BizException(ErrorCode.DUPLICATE_DATA, "이미 등록된 증권번호입니다");
|
||||||
|
}
|
||||||
|
ContractVO vo = new ContractVO();
|
||||||
|
vo.setAgentId(req.getAgentId());
|
||||||
|
vo.setProductId(req.getProductId());
|
||||||
|
vo.setPolicyNo(req.getPolicyNo());
|
||||||
|
vo.setContractorName(req.getContractorName());
|
||||||
|
vo.setInsuredName(req.getInsuredName());
|
||||||
|
vo.setPremium(req.getPremium());
|
||||||
|
vo.setPayCycle(req.getPayCycle());
|
||||||
|
vo.setPayPeriod(req.getPayPeriod());
|
||||||
|
vo.setCoveragePeriod(req.getCoveragePeriod());
|
||||||
|
vo.setStatus("ACTIVE");
|
||||||
|
vo.setContractDate(req.getContractDate());
|
||||||
|
vo.setEffectiveDate(req.getEffectiveDate());
|
||||||
|
mapper.insert(vo);
|
||||||
|
return vo.getContractId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void update(Long contractId, ContractSaveReq req) {
|
||||||
|
ContractVO vo = mapper.selectById(contractId);
|
||||||
|
if (vo == null) throw new BizException(ErrorCode.NOT_FOUND);
|
||||||
|
vo.setAgentId(req.getAgentId());
|
||||||
|
vo.setProductId(req.getProductId());
|
||||||
|
vo.setContractorName(req.getContractorName());
|
||||||
|
vo.setInsuredName(req.getInsuredName());
|
||||||
|
vo.setPremium(req.getPremium());
|
||||||
|
vo.setPayCycle(req.getPayCycle());
|
||||||
|
vo.setPayPeriod(req.getPayPeriod());
|
||||||
|
vo.setCoveragePeriod(req.getCoveragePeriod());
|
||||||
|
vo.setEffectiveDate(req.getEffectiveDate());
|
||||||
|
mapper.update(vo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void changeStatus(Long contractId, String status) {
|
||||||
|
mapper.updateStatus(contractId, status);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package com.ga.api.service.settle;
|
||||||
|
|
||||||
|
import com.ga.common.exception.BizException;
|
||||||
|
import com.ga.common.exception.ErrorCode;
|
||||||
|
import com.ga.common.model.PageResponse;
|
||||||
|
import com.ga.common.util.SecurityUtil;
|
||||||
|
import com.ga.core.mapper.settle.SettleMasterMapper;
|
||||||
|
import com.ga.core.vo.settle.SettleMasterResp;
|
||||||
|
import com.ga.core.vo.settle.SettleMasterVO;
|
||||||
|
import com.ga.core.vo.settle.SettleSearchParam;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SettleService {
|
||||||
|
|
||||||
|
private final SettleMasterMapper masterMapper;
|
||||||
|
|
||||||
|
public PageResponse<SettleMasterResp> list(SettleSearchParam param) {
|
||||||
|
param.startPage();
|
||||||
|
return PageResponse.of(masterMapper.selectList(param));
|
||||||
|
}
|
||||||
|
|
||||||
|
public SettleMasterResp detail(Long agentId, String settleMonth) {
|
||||||
|
SettleMasterResp r = masterMapper.selectDetail(agentId, settleMonth);
|
||||||
|
if (r == null) throw new BizException(ErrorCode.NOT_FOUND);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> summary(String settleMonth) {
|
||||||
|
return masterMapper.selectMonthSummary(settleMonth);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Map<String, Object>> orgSummary(String settleMonth) {
|
||||||
|
return masterMapper.selectOrgSummary(settleMonth);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void confirm(Long settleId) {
|
||||||
|
SettleMasterVO vo = masterMapper.selectById(settleId);
|
||||||
|
if (vo == null) throw new BizException(ErrorCode.NOT_FOUND);
|
||||||
|
if ("CONFIRMED".equals(vo.getStatus()) || "PAID".equals(vo.getStatus())) {
|
||||||
|
throw new BizException(ErrorCode.ALREADY_CONFIRMED);
|
||||||
|
}
|
||||||
|
masterMapper.updateStatus(settleId, "CONFIRMED", SecurityUtil.getCurrentUserId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void hold(Long settleId, String reason) {
|
||||||
|
SettleMasterVO vo = masterMapper.selectById(settleId);
|
||||||
|
if (vo == null) throw new BizException(ErrorCode.NOT_FOUND);
|
||||||
|
if ("PAID".equals(vo.getStatus())) {
|
||||||
|
throw new BizException(ErrorCode.NOT_PERMITTED, "지급완료된 정산은 보류할 수 없습니다");
|
||||||
|
}
|
||||||
|
masterMapper.updateHold(settleId, reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void release(Long settleId) {
|
||||||
|
SettleMasterVO vo = masterMapper.selectById(settleId);
|
||||||
|
if (vo == null) throw new BizException(ErrorCode.NOT_FOUND);
|
||||||
|
if (!"HOLD".equals(vo.getStatus())) {
|
||||||
|
throw new BizException(ErrorCode.INVALID_STATUS, "보류 상태가 아닙니다");
|
||||||
|
}
|
||||||
|
masterMapper.updateStatus(settleId, "CALCULATED", SecurityUtil.getCurrentUserId());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
package com.ga.api.service.system;
|
||||||
|
|
||||||
|
import com.ga.common.exception.BizException;
|
||||||
|
import com.ga.common.exception.ErrorCode;
|
||||||
|
import com.ga.common.model.PageResponse;
|
||||||
|
import com.ga.common.system.SystemConfigService;
|
||||||
|
import com.ga.core.mapper.user.UserMapper;
|
||||||
|
import com.ga.core.vo.user.UserResp;
|
||||||
|
import com.ga.core.vo.user.UserSaveReq;
|
||||||
|
import com.ga.core.vo.user.UserSearchParam;
|
||||||
|
import com.ga.core.vo.user.UserVO;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class UserService {
|
||||||
|
|
||||||
|
private final UserMapper mapper;
|
||||||
|
private final PasswordEncoder passwordEncoder;
|
||||||
|
private final SystemConfigService configService;
|
||||||
|
|
||||||
|
public PageResponse<UserResp> list(UserSearchParam param) {
|
||||||
|
param.startPage();
|
||||||
|
return PageResponse.of(mapper.selectList(param));
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserResp detail(Long userId) {
|
||||||
|
UserResp r = mapper.selectDetailById(userId);
|
||||||
|
if (r == null) throw new BizException(ErrorCode.NOT_FOUND);
|
||||||
|
r.setRoleCodes(mapper.selectRoleCodes(userId));
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Long create(UserSaveReq req) {
|
||||||
|
if (mapper.selectByLoginId(req.getLoginId()) != null) {
|
||||||
|
throw new BizException(ErrorCode.DUPLICATE_DATA, "이미 사용 중인 로그인 ID입니다");
|
||||||
|
}
|
||||||
|
String initialPwd = req.getPassword();
|
||||||
|
if (initialPwd == null || initialPwd.isBlank()) {
|
||||||
|
initialPwd = "init1234!"; // 임시 — 로그인 후 변경 강제
|
||||||
|
}
|
||||||
|
UserVO vo = new UserVO();
|
||||||
|
vo.setLoginId(req.getLoginId());
|
||||||
|
vo.setPassword(passwordEncoder.encode(initialPwd));
|
||||||
|
vo.setUserName(req.getUserName());
|
||||||
|
vo.setEmail(req.getEmail());
|
||||||
|
vo.setPhone(req.getPhone());
|
||||||
|
vo.setAgentId(req.getAgentId());
|
||||||
|
vo.setStatus(req.getStatus() != null ? req.getStatus() : "ACTIVE");
|
||||||
|
mapper.insert(vo);
|
||||||
|
|
||||||
|
if (req.getRoleIds() != null) {
|
||||||
|
req.getRoleIds().forEach(rid -> mapper.insertUserRole(vo.getUserId(), rid));
|
||||||
|
}
|
||||||
|
return vo.getUserId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void update(Long userId, UserSaveReq req) {
|
||||||
|
UserVO old = mapper.selectById(userId);
|
||||||
|
if (old == null) throw new BizException(ErrorCode.NOT_FOUND);
|
||||||
|
UserVO vo = new UserVO();
|
||||||
|
vo.setUserId(userId);
|
||||||
|
vo.setUserName(req.getUserName());
|
||||||
|
vo.setEmail(req.getEmail());
|
||||||
|
vo.setPhone(req.getPhone());
|
||||||
|
vo.setAgentId(req.getAgentId());
|
||||||
|
vo.setStatus(req.getStatus());
|
||||||
|
mapper.update(vo);
|
||||||
|
|
||||||
|
if (req.getRoleIds() != null) {
|
||||||
|
mapper.deleteUserRoles(userId);
|
||||||
|
req.getRoleIds().forEach(rid -> mapper.insertUserRole(userId, rid));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void resetPassword(Long userId) {
|
||||||
|
int min = configService.getInt("PASSWORD_MIN_LEN", 8);
|
||||||
|
String temp = "reset" + System.currentTimeMillis() % 1_000_000;
|
||||||
|
if (temp.length() < min) temp = temp + "!@#";
|
||||||
|
mapper.updatePassword(userId, passwordEncoder.encode(temp));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void unlock(Long userId) {
|
||||||
|
mapper.resetFailCount(userId);
|
||||||
|
mapper.updateStatus(userId, "ACTIVE");
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> roleCodes(Long userId) {
|
||||||
|
return mapper.selectRoleCodes(userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.ga.batch;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@SpringBootApplication(scanBasePackages = {"com.ga"})
|
||||||
|
public class GaBatchApplication {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(GaBatchApplication.class, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package com.ga.batch.config;
|
||||||
|
|
||||||
|
import com.ga.batch.settlement.SettlementContext;
|
||||||
|
import org.springframework.batch.core.Job;
|
||||||
|
import org.springframework.batch.core.Step;
|
||||||
|
import org.springframework.batch.core.configuration.annotation.JobScope;
|
||||||
|
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.Tasklet;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.transaction.PlatformTransactionManager;
|
||||||
|
|
||||||
|
import com.ga.batch.settlement.step.*;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class BatchConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@JobScope
|
||||||
|
public SettlementContext settlementContext(@Value("#{jobParameters['settleMonth']}") String settleMonth,
|
||||||
|
@Value("#{jobParameters['jobLogId'] ?: -1L}") Long jobLogId) {
|
||||||
|
SettlementContext ctx = new SettlementContext();
|
||||||
|
ctx.setSettleMonth(settleMonth);
|
||||||
|
ctx.setJobLogId(jobLogId);
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public Job monthlySettlementJob(JobRepository jobRepository,
|
||||||
|
PlatformTransactionManager tx,
|
||||||
|
ReceiveDataStep step1,
|
||||||
|
TransformDataStep step2,
|
||||||
|
ReconcileStep step3,
|
||||||
|
CalcRecruitStep step4,
|
||||||
|
CalcMaintainStep step5,
|
||||||
|
ChargebackExceptionStep step6,
|
||||||
|
CalcOverrideStep step7,
|
||||||
|
AggregateStep step8) {
|
||||||
|
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))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Step taskletStep(String name, Tasklet tasklet,
|
||||||
|
JobRepository jobRepository, PlatformTransactionManager tx) {
|
||||||
|
return new StepBuilder(name, jobRepository)
|
||||||
|
.tasklet(tasklet, tx)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package com.ga.batch.controller;
|
||||||
|
|
||||||
|
import com.ga.common.exception.BizException;
|
||||||
|
import com.ga.common.exception.ErrorCode;
|
||||||
|
import com.ga.common.model.ApiResponse;
|
||||||
|
import com.ga.core.mapper.settle.BatchJobLogMapper;
|
||||||
|
import com.ga.core.vo.settle.BatchJobLogVO;
|
||||||
|
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.*;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ga-batch 프로세스 내부 트리거. ga-api 가 HTTP 로 호출.
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/internal/jobs")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class JobLauncherController {
|
||||||
|
|
||||||
|
private final JobLauncher jobLauncher;
|
||||||
|
private final Job monthlySettlementJob;
|
||||||
|
private final BatchJobLogMapper jobLogMapper;
|
||||||
|
|
||||||
|
@PostMapping("/monthly-settlement")
|
||||||
|
public ApiResponse<Long> runMonthlySettlement(@RequestBody Map<String, String> body) throws Exception {
|
||||||
|
String settleMonth = body.get("settleMonth");
|
||||||
|
if (settleMonth == null || !settleMonth.matches("\\d{6}")) {
|
||||||
|
throw new BizException(ErrorCode.INVALID_PARAMETER, "settleMonth (yyyyMM)");
|
||||||
|
}
|
||||||
|
|
||||||
|
BatchJobLogVO log = new BatchJobLogVO();
|
||||||
|
log.setJobName("MONTHLY_SETTLEMENT");
|
||||||
|
log.setSettleMonth(settleMonth);
|
||||||
|
log.setStatus("STARTED");
|
||||||
|
jobLogMapper.insert(log);
|
||||||
|
|
||||||
|
JobParameters params = new JobParametersBuilder()
|
||||||
|
.addString("settleMonth", settleMonth)
|
||||||
|
.addLong("jobLogId", log.getJobId())
|
||||||
|
.addLong("ts", System.currentTimeMillis())
|
||||||
|
.toJobParameters();
|
||||||
|
|
||||||
|
jobLauncher.run(monthlySettlementJob, params);
|
||||||
|
return ApiResponse.ok(log.getJobId());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.ga.batch.settlement;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Job 단위 컨텍스트. Step 간 공유 상태.
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class SettlementContext {
|
||||||
|
private String settleMonth; // yyyyMM
|
||||||
|
private Long jobLogId;
|
||||||
|
private int receivedCount;
|
||||||
|
private int transformedCount;
|
||||||
|
private int reconcileDiffCount;
|
||||||
|
private int recruitCount;
|
||||||
|
private int maintainCount;
|
||||||
|
private int chargebackCount;
|
||||||
|
private int overrideCount;
|
||||||
|
private int aggregateCount;
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package com.ga.batch.settlement.calc;
|
||||||
|
|
||||||
|
import com.ga.common.util.MoneyUtil;
|
||||||
|
import com.ga.core.mapper.org.AgentMapper;
|
||||||
|
import com.ga.core.mapper.rule.RuleMapper;
|
||||||
|
import com.ga.core.vo.ledger.LedgerVO;
|
||||||
|
import com.ga.core.vo.org.AgentVO;
|
||||||
|
import com.ga.core.vo.product.ContractResp;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 수수료 계산 핵심 로직.
|
||||||
|
* - 모집수수료: premium × company_rate × payout_rate
|
||||||
|
* - 유지수수료: 동일 공식, 회차에 따른 commission_year 만 다름
|
||||||
|
* - 모든 금액은 BigDecimal HALF_UP, 소수점 2자리
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CommissionCalculator {
|
||||||
|
|
||||||
|
private final RuleMapper ruleMapper;
|
||||||
|
private final AgentMapper agentMapper;
|
||||||
|
|
||||||
|
public LedgerVO calcRecruitLedger(ContractResp c, String settleMonth) {
|
||||||
|
return buildLedger(c, settleMonth, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LedgerVO calcMaintainLedger(ContractResp c, String settleMonth, int commissionYear) {
|
||||||
|
return buildLedger(c, settleMonth, commissionYear);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LedgerVO buildLedger(ContractResp c, String settleMonth, int year) {
|
||||||
|
AgentVO agent = agentMapper.selectById(c.getAgentId());
|
||||||
|
if (agent == null) return null;
|
||||||
|
LocalDate baseDate = c.getContractDate() != null ? c.getContractDate() : LocalDate.now();
|
||||||
|
|
||||||
|
BigDecimal companyRate = ruleMapper.findCompanyRate(c.getProductId(), year, baseDate);
|
||||||
|
if (companyRate == null) companyRate = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
BigDecimal payoutRate = ruleMapper.findPayoutRate(
|
||||||
|
agent.getGradeId(), c.getInsuranceType(), year, baseDate);
|
||||||
|
if (payoutRate == null) payoutRate = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
BigDecimal premium = MoneyUtil.zero(c.getPremium());
|
||||||
|
BigDecimal companyAmount = MoneyUtil.pct(premium, companyRate);
|
||||||
|
BigDecimal agentAmount = MoneyUtil.pct(companyAmount, payoutRate);
|
||||||
|
BigDecimal taxAmount = MoneyUtil.tax(agentAmount);
|
||||||
|
|
||||||
|
LedgerVO led = new LedgerVO();
|
||||||
|
led.setContractId(c.getContractId());
|
||||||
|
led.setAgentId(c.getAgentId());
|
||||||
|
led.setPolicyNo(c.getPolicyNo());
|
||||||
|
led.setCompanyCode(c.getCompanyCode());
|
||||||
|
led.setProductCode(c.getProductCode());
|
||||||
|
led.setInsuranceType(c.getInsuranceType());
|
||||||
|
led.setCommissionYear(year);
|
||||||
|
led.setPremium(premium);
|
||||||
|
led.setPayMethod(c.getPayCycle());
|
||||||
|
led.setCompanyRate(companyRate);
|
||||||
|
led.setCompanyAmount(companyAmount);
|
||||||
|
led.setPayoutRate(payoutRate);
|
||||||
|
led.setAgentAmount(agentAmount);
|
||||||
|
led.setTaxAmount(taxAmount);
|
||||||
|
led.setSettleMonth(settleMonth);
|
||||||
|
led.setStatus("CALCULATED");
|
||||||
|
return led;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.ga.batch.settlement.receive;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 보험사별 데이터 수신 전략. (Excel/CSV/EDI/API)
|
||||||
|
*/
|
||||||
|
public interface DataReceiver {
|
||||||
|
/** 이 receiver 가 해당 보험사를 지원하는지 */
|
||||||
|
boolean supports(String companyCode);
|
||||||
|
|
||||||
|
/** 데이터 수신 → raw_commission_data 적재. 처리한 행 수 반환 */
|
||||||
|
int receive(String companyCode, String settleMonth);
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.ga.batch.settlement.receive;
|
||||||
|
|
||||||
|
import com.ga.core.mapper.receive.ReceiveMapper;
|
||||||
|
import com.ga.core.vo.receive.CompanyProfileVO;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 수동 업로드된 데이터를 사용하는 기본 Receiver.
|
||||||
|
* (이미 화면에서 업로드된 raw_commission_data 가 있으면 그대로 사용)
|
||||||
|
*
|
||||||
|
* 운영에서 FTP/API Receiver 추가 시 이 인터페이스 구현체로 추가.
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ManualReceiver implements DataReceiver {
|
||||||
|
|
||||||
|
private final ReceiveMapper mapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(String companyCode) {
|
||||||
|
CompanyProfileVO p = mapper.selectProfile(companyCode);
|
||||||
|
return p == null || "MANUAL".equalsIgnoreCase(p.getReceiveMethod());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int receive(String companyCode, String settleMonth) {
|
||||||
|
// 수동 업로드는 별도 작업 없음. 기존 raw 데이터 건수만 반환.
|
||||||
|
return mapper.selectRawList(companyCode, settleMonth, "PENDING").size();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
package com.ga.batch.settlement.step;
|
||||||
|
|
||||||
|
import com.ga.batch.settlement.SettlementContext;
|
||||||
|
import com.ga.common.system.SystemConfigService;
|
||||||
|
import com.ga.common.util.MoneyUtil;
|
||||||
|
import com.ga.core.mapper.ledger.ExceptionLedgerMapper;
|
||||||
|
import com.ga.core.mapper.ledger.MaintainLedgerMapper;
|
||||||
|
import com.ga.core.mapper.ledger.RecruitLedgerMapper;
|
||||||
|
import com.ga.core.mapper.org.AgentMapper;
|
||||||
|
import com.ga.core.mapper.settle.ChargebackMapper;
|
||||||
|
import com.ga.core.mapper.settle.OverrideSettleMapper;
|
||||||
|
import com.ga.core.mapper.settle.SettleMasterMapper;
|
||||||
|
import com.ga.core.vo.org.AgentResp;
|
||||||
|
import com.ga.core.vo.org.AgentSearchParam;
|
||||||
|
import com.ga.core.vo.settle.SettleMasterVO;
|
||||||
|
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.math.BigDecimal;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Step 8 — aggregate: 설계사별 통합 집계 → settle_master UPSERT (세금 자동 계산)
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@JobScope
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AggregateStep implements Tasklet {
|
||||||
|
|
||||||
|
private final SettlementContext ctx;
|
||||||
|
private final AgentMapper agentMapper;
|
||||||
|
private final RecruitLedgerMapper recruitMapper;
|
||||||
|
private final MaintainLedgerMapper maintainMapper;
|
||||||
|
private final ExceptionLedgerMapper exceptionMapper;
|
||||||
|
private final OverrideSettleMapper overrideMapper;
|
||||||
|
private final ChargebackMapper chargebackMapper;
|
||||||
|
private final SettleMasterMapper settleMapper;
|
||||||
|
private final SystemConfigService configService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {
|
||||||
|
log.info("[Step8 aggregate] settleMonth={}", ctx.getSettleMonth());
|
||||||
|
|
||||||
|
Map<Long, BigDecimal> recruitMap = toMap(recruitMapper.aggregateByAgent(ctx.getSettleMonth()));
|
||||||
|
Map<Long, BigDecimal> maintainMap = toMap(maintainMapper.aggregateByAgent(ctx.getSettleMonth()));
|
||||||
|
Map<Long, BigDecimal> chargebackMap= toMap(chargebackMapper.aggregateByAgent(ctx.getSettleMonth()));
|
||||||
|
Map<Long, BigDecimal> overrideMap = toMap(overrideMapper.aggregateByUpper(ctx.getSettleMonth()));
|
||||||
|
// 예외는 PLUS/MINUS 분리
|
||||||
|
Map<Long, BigDecimal> excPlus = new HashMap<>();
|
||||||
|
Map<Long, BigDecimal> excMinus = new HashMap<>();
|
||||||
|
for (Map<String, Object> row : exceptionMapper.aggregateByAgent(ctx.getSettleMonth())) {
|
||||||
|
Long agentId = ((Number) row.get("agentId")).longValue();
|
||||||
|
excPlus.put(agentId, toBd(row.get("plus")));
|
||||||
|
excMinus.put(agentId, toBd(row.get("minus")));
|
||||||
|
}
|
||||||
|
|
||||||
|
BigDecimal taxRate = configService.getDecimal("TAX_RATE", new BigDecimal("3.3"));
|
||||||
|
|
||||||
|
// 합집합 agent_id
|
||||||
|
java.util.Set<Long> agentIds = new java.util.HashSet<>();
|
||||||
|
agentIds.addAll(recruitMap.keySet());
|
||||||
|
agentIds.addAll(maintainMap.keySet());
|
||||||
|
agentIds.addAll(chargebackMap.keySet());
|
||||||
|
agentIds.addAll(overrideMap.keySet());
|
||||||
|
agentIds.addAll(excPlus.keySet());
|
||||||
|
agentIds.addAll(excMinus.keySet());
|
||||||
|
|
||||||
|
// 활성 설계사 누락 보충
|
||||||
|
AgentSearchParam ap = new AgentSearchParam();
|
||||||
|
ap.setStatus("ACTIVE"); ap.setPageSize(Integer.MAX_VALUE);
|
||||||
|
for (AgentResp a : agentMapper.selectList(ap)) agentIds.add(a.getAgentId());
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
for (Long agentId : agentIds) {
|
||||||
|
BigDecimal recruit = recruitMap.getOrDefault(agentId, BigDecimal.ZERO);
|
||||||
|
BigDecimal maintain = maintainMap.getOrDefault(agentId, BigDecimal.ZERO);
|
||||||
|
BigDecimal cb = chargebackMap.getOrDefault(agentId, BigDecimal.ZERO);
|
||||||
|
BigDecimal ov = overrideMap.getOrDefault(agentId, BigDecimal.ZERO);
|
||||||
|
BigDecimal ep = excPlus.getOrDefault(agentId, BigDecimal.ZERO);
|
||||||
|
BigDecimal em = excMinus.getOrDefault(agentId, BigDecimal.ZERO);
|
||||||
|
|
||||||
|
BigDecimal gross = MoneyUtil.add(MoneyUtil.add(MoneyUtil.add(recruit, maintain), ov), ep)
|
||||||
|
.subtract(cb).subtract(em);
|
||||||
|
BigDecimal tax = MoneyUtil.tax(gross, taxRate);
|
||||||
|
BigDecimal net = MoneyUtil.sub(gross, tax);
|
||||||
|
|
||||||
|
SettleMasterVO vo = new SettleMasterVO();
|
||||||
|
vo.setAgentId(agentId);
|
||||||
|
vo.setSettleMonth(ctx.getSettleMonth());
|
||||||
|
vo.setRecruitTotal(recruit);
|
||||||
|
vo.setMaintainTotal(maintain);
|
||||||
|
vo.setExceptionPlus(ep);
|
||||||
|
vo.setExceptionMinus(em);
|
||||||
|
vo.setOverrideTotal(ov);
|
||||||
|
vo.setChargebackTotal(cb);
|
||||||
|
vo.setGrossAmount(gross);
|
||||||
|
vo.setTaxAmount(tax);
|
||||||
|
vo.setNetAmount(net);
|
||||||
|
vo.setStatus("CALCULATED");
|
||||||
|
settleMapper.upsert(vo);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
ctx.setAggregateCount(count);
|
||||||
|
log.info("[Step8] settle master upserted: {}", count);
|
||||||
|
return RepeatStatus.FINISHED;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<Long, BigDecimal> toMap(List<Map<String, Object>> rows) {
|
||||||
|
Map<Long, BigDecimal> m = new HashMap<>();
|
||||||
|
for (Map<String, Object> r : rows) {
|
||||||
|
Long id = ((Number) r.get("agentId")).longValue();
|
||||||
|
m.put(id, toBd(r.get("total")));
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal toBd(Object v) {
|
||||||
|
if (v == null) return BigDecimal.ZERO;
|
||||||
|
if (v instanceof BigDecimal bd) return bd;
|
||||||
|
return new BigDecimal(v.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
package com.ga.batch.settlement.step;
|
||||||
|
|
||||||
|
import com.ga.batch.settlement.SettlementContext;
|
||||||
|
import com.ga.batch.settlement.calc.CommissionCalculator;
|
||||||
|
import com.ga.common.util.DateUtil;
|
||||||
|
import com.ga.common.util.MoneyUtil;
|
||||||
|
import com.ga.core.mapper.ledger.MaintainLedgerMapper;
|
||||||
|
import com.ga.core.mapper.product.ContractMapper;
|
||||||
|
import com.ga.core.vo.ledger.LedgerVO;
|
||||||
|
import com.ga.core.vo.product.ContractResp;
|
||||||
|
import com.ga.core.vo.product.ContractSearchParam;
|
||||||
|
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 5 — calcMaintain: 유지수수료 계산 (2회차~)
|
||||||
|
*
|
||||||
|
* 유지수수료: 계약일로부터 N개월 경과한 활동 계약 (status=ACTIVE) 대상.
|
||||||
|
* - 1회차는 모집수수료에서 처리하므로 N >= 1 인 계약 대상.
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@JobScope
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CalcMaintainStep implements Tasklet {
|
||||||
|
|
||||||
|
private final SettlementContext ctx;
|
||||||
|
private final ContractMapper contractMapper;
|
||||||
|
private final MaintainLedgerMapper maintainMapper;
|
||||||
|
private final CommissionCalculator calculator;
|
||||||
|
|
||||||
|
private static final int BATCH_SIZE = 1000;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {
|
||||||
|
log.info("[Step5 calcMaintain] settleMonth={}", ctx.getSettleMonth());
|
||||||
|
|
||||||
|
ContractSearchParam param = new ContractSearchParam();
|
||||||
|
param.setStatus("ACTIVE");
|
||||||
|
param.setPageSize(Integer.MAX_VALUE);
|
||||||
|
List<ContractResp> contracts = contractMapper.selectList(param);
|
||||||
|
|
||||||
|
maintainMapper.deleteBySettleMonth(ctx.getSettleMonth());
|
||||||
|
|
||||||
|
List<LedgerVO> batch = new ArrayList<>(BATCH_SIZE);
|
||||||
|
int total = 0;
|
||||||
|
|
||||||
|
for (ContractResp c : contracts) {
|
||||||
|
if (c.getContractDate() == null) continue;
|
||||||
|
String contractMonth = DateUtil.getSettleMonth(c.getContractDate());
|
||||||
|
int monthsElapsed = DateUtil.calcMonthDiff(contractMonth, ctx.getSettleMonth());
|
||||||
|
if (monthsElapsed < 1) continue; // 모집회차는 step4에서 처리
|
||||||
|
int year = (monthsElapsed / 12) + 1; // 1년차/2년차/...
|
||||||
|
if (year > 5) continue; // 통상 5년차까지
|
||||||
|
|
||||||
|
// 납입주기 매칭 (월납이면 매월, 분기납이면 3개월마다 ...)
|
||||||
|
if (!matchesPayCycle(c.getPayCycle(), monthsElapsed)) continue;
|
||||||
|
|
||||||
|
LedgerVO led = calculator.calcMaintainLedger(c, ctx.getSettleMonth(), year);
|
||||||
|
if (led == null || MoneyUtil.isZero(led.getAgentAmount())) continue;
|
||||||
|
batch.add(led);
|
||||||
|
if (batch.size() >= BATCH_SIZE) {
|
||||||
|
maintainMapper.batchInsert(batch);
|
||||||
|
total += batch.size();
|
||||||
|
batch.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!batch.isEmpty()) {
|
||||||
|
maintainMapper.batchInsert(batch);
|
||||||
|
total += batch.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.setMaintainCount(total);
|
||||||
|
log.info("[Step5] maintain ledger inserted: {}", total);
|
||||||
|
return RepeatStatus.FINISHED;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean matchesPayCycle(String cycle, int monthsElapsed) {
|
||||||
|
if (cycle == null) return true;
|
||||||
|
return switch (cycle) {
|
||||||
|
case "MONTHLY" -> true;
|
||||||
|
case "QUARTERLY" -> monthsElapsed % 3 == 0;
|
||||||
|
case "ANNUAL" -> monthsElapsed % 12 == 0;
|
||||||
|
case "LUMP" -> false; // 일시납은 모집회차만
|
||||||
|
default -> true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
package com.ga.batch.settlement.step;
|
||||||
|
|
||||||
|
import com.ga.batch.settlement.SettlementContext;
|
||||||
|
import com.ga.common.util.MoneyUtil;
|
||||||
|
import com.ga.core.mapper.org.AgentMapper;
|
||||||
|
import com.ga.core.mapper.org.OrganizationMapper;
|
||||||
|
import com.ga.core.mapper.rule.RuleMapper;
|
||||||
|
import com.ga.core.mapper.settle.OverrideSettleMapper;
|
||||||
|
import com.ga.core.vo.org.AgentVO;
|
||||||
|
import com.ga.core.vo.rule.OverrideRuleVO;
|
||||||
|
import com.ga.core.vo.settle.OverrideSettleVO;
|
||||||
|
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.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Step 7 — calcOverride: 조직 트리 기반 오버라이드 계산
|
||||||
|
*
|
||||||
|
* 단순화: 정산월의 모집+유지 합계 × override_pct → 상위 직급에게 분배.
|
||||||
|
* 실제로는 from_grade → to_grade 매트릭스로 다단계 분배해야 함.
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@JobScope
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CalcOverrideStep implements Tasklet {
|
||||||
|
|
||||||
|
private final SettlementContext ctx;
|
||||||
|
private final RuleMapper ruleMapper;
|
||||||
|
private final AgentMapper agentMapper;
|
||||||
|
private final OrganizationMapper organizationMapper;
|
||||||
|
private final OverrideSettleMapper overrideMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {
|
||||||
|
log.info("[Step7 calcOverride] settleMonth={}", ctx.getSettleMonth());
|
||||||
|
|
||||||
|
overrideMapper.deleteBySettleMonth(ctx.getSettleMonth());
|
||||||
|
List<OverrideRuleVO> rules = ruleMapper.selectOverrideRules();
|
||||||
|
|
||||||
|
// 정산월의 활성 오버라이드 규칙만 (effective_from 기준)
|
||||||
|
LocalDate baseDate = LocalDate.parse(ctx.getSettleMonth().substring(0,4) + "-" + ctx.getSettleMonth().substring(4) + "-01");
|
||||||
|
rules.removeIf(r -> r.getEffectiveFrom() != null && r.getEffectiveFrom().isAfter(baseDate));
|
||||||
|
|
||||||
|
List<OverrideSettleVO> records = new ArrayList<>();
|
||||||
|
// 단순화: 모든 ACTIVE 설계사를 조회하고, 본인의 grade 가 from_grade 인 규칙에 대해 상위 조직장 검색
|
||||||
|
com.ga.core.vo.org.AgentSearchParam ap = new com.ga.core.vo.org.AgentSearchParam();
|
||||||
|
ap.setStatus("ACTIVE");
|
||||||
|
ap.setPageSize(Integer.MAX_VALUE);
|
||||||
|
List<com.ga.core.vo.org.AgentResp> agents = agentMapper.selectList(ap);
|
||||||
|
|
||||||
|
for (com.ga.core.vo.org.AgentResp lower : agents) {
|
||||||
|
for (OverrideRuleVO rule : rules) {
|
||||||
|
if (!java.util.Objects.equals(rule.getFromGrade(), lower.getGradeId())) continue;
|
||||||
|
Long upperAgentId = findUpperAgentByGrade(lower.getOrgId(), rule.getToGrade());
|
||||||
|
if (upperAgentId == null) continue;
|
||||||
|
|
||||||
|
BigDecimal base = sumLowerCommission(lower.getAgentId(), ctx.getSettleMonth());
|
||||||
|
BigDecimal amount = MoneyUtil.pct(base, rule.getOverridePct());
|
||||||
|
if (rule.getCapAmount() != null && amount.compareTo(rule.getCapAmount()) > 0) {
|
||||||
|
amount = rule.getCapAmount();
|
||||||
|
}
|
||||||
|
if (MoneyUtil.isZero(amount)) continue;
|
||||||
|
|
||||||
|
OverrideSettleVO os = new OverrideSettleVO();
|
||||||
|
os.setUpperAgentId(upperAgentId);
|
||||||
|
os.setLowerAgentId(lower.getAgentId());
|
||||||
|
os.setSettleMonth(ctx.getSettleMonth());
|
||||||
|
os.setBaseAmount(base);
|
||||||
|
os.setOverridePct(rule.getOverridePct());
|
||||||
|
os.setOverrideAmount(amount);
|
||||||
|
records.add(os);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!records.isEmpty()) overrideMapper.insertBatch(records);
|
||||||
|
ctx.setOverrideCount(records.size());
|
||||||
|
log.info("[Step7] override inserted: {}", records.size());
|
||||||
|
return RepeatStatus.FINISHED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 하위 설계사의 정산월 모집+유지 합계 (단순화: 모집 원장 합으로 대체) */
|
||||||
|
private BigDecimal sumLowerCommission(Long agentId, String settleMonth) {
|
||||||
|
// 실제로는 recruit + maintain agent별 합계 필요. 여기서는 0 반환 (실제 운영 시 별도 sumByAgentMonth 추가)
|
||||||
|
return BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 동일 조직 트리에서 to_grade 직급의 상위 설계사 찾기 (단순화: org 트리 상위 첫 번째) */
|
||||||
|
private Long findUpperAgentByGrade(Long orgId, Long toGrade) {
|
||||||
|
if (orgId == null) return null;
|
||||||
|
// 조직 트리 상향 탐색은 운영 단계에서 추가. 여기서는 동일 조직 내 toGrade 1명 반환.
|
||||||
|
com.ga.core.vo.org.AgentSearchParam p = new com.ga.core.vo.org.AgentSearchParam();
|
||||||
|
p.setOrgId(orgId);
|
||||||
|
p.setGradeId(toGrade);
|
||||||
|
p.setStatus("ACTIVE");
|
||||||
|
p.setPageSize(1);
|
||||||
|
List<com.ga.core.vo.org.AgentResp> upper = agentMapper.selectList(p);
|
||||||
|
return upper.isEmpty() ? null : upper.get(0).getAgentId();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package com.ga.batch.settlement.step;
|
||||||
|
|
||||||
|
import com.ga.batch.settlement.SettlementContext;
|
||||||
|
import com.ga.batch.settlement.calc.CommissionCalculator;
|
||||||
|
import com.ga.common.util.MoneyUtil;
|
||||||
|
import com.ga.core.mapper.ledger.RecruitLedgerMapper;
|
||||||
|
import com.ga.core.mapper.product.ContractMapper;
|
||||||
|
import com.ga.core.vo.ledger.LedgerVO;
|
||||||
|
import com.ga.core.vo.product.ContractResp;
|
||||||
|
import com.ga.core.vo.product.ContractSearchParam;
|
||||||
|
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.math.BigDecimal;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Step 4 — calcRecruit: 모집 수수료 계산 (계약 첫 회차)
|
||||||
|
*
|
||||||
|
* 원칙: 계약시점의 commission_rate / payout_rule 적용 (effective_from 기준)
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@JobScope
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CalcRecruitStep implements Tasklet {
|
||||||
|
|
||||||
|
private final SettlementContext ctx;
|
||||||
|
private final ContractMapper contractMapper;
|
||||||
|
private final RecruitLedgerMapper recruitMapper;
|
||||||
|
private final CommissionCalculator calculator;
|
||||||
|
|
||||||
|
private static final int BATCH_SIZE = 1000;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {
|
||||||
|
log.info("[Step4 calcRecruit] settleMonth={}", ctx.getSettleMonth());
|
||||||
|
|
||||||
|
// 모집수수료 대상: 계약일이 정산월에 해당하는 계약
|
||||||
|
ContractSearchParam param = new ContractSearchParam();
|
||||||
|
param.setStartDate(ctx.getSettleMonth().substring(0, 4) + "-" + ctx.getSettleMonth().substring(4) + "-01");
|
||||||
|
param.setEndDate(param.getStartDate());
|
||||||
|
param.setPageSize(Integer.MAX_VALUE);
|
||||||
|
List<ContractResp> contracts = contractMapper.selectList(param);
|
||||||
|
|
||||||
|
// 정산월 모집원장 초기화 (재계산 대비)
|
||||||
|
recruitMapper.deleteBySettleMonth(ctx.getSettleMonth());
|
||||||
|
|
||||||
|
List<LedgerVO> batch = new ArrayList<>(BATCH_SIZE);
|
||||||
|
int total = 0;
|
||||||
|
for (ContractResp c : contracts) {
|
||||||
|
LedgerVO led = calculator.calcRecruitLedger(c, ctx.getSettleMonth());
|
||||||
|
if (led == null || MoneyUtil.isZero(led.getAgentAmount())) continue;
|
||||||
|
batch.add(led);
|
||||||
|
if (batch.size() >= BATCH_SIZE) {
|
||||||
|
recruitMapper.batchInsert(batch);
|
||||||
|
total += batch.size();
|
||||||
|
batch.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!batch.isEmpty()) {
|
||||||
|
recruitMapper.batchInsert(batch);
|
||||||
|
total += batch.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.setRecruitCount(total);
|
||||||
|
log.info("[Step4] recruit ledger inserted: {}", total);
|
||||||
|
return RepeatStatus.FINISHED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 정산월의 첫날 / 마지막날 — calcRecruit 대상 기간 */
|
||||||
|
private static String[] monthRange(String yyyymm) {
|
||||||
|
int year = Integer.parseInt(yyyymm.substring(0, 4));
|
||||||
|
int month = Integer.parseInt(yyyymm.substring(4));
|
||||||
|
java.time.YearMonth ym = java.time.YearMonth.of(year, month);
|
||||||
|
return new String[]{ym.atDay(1).toString(), ym.atEndOfMonth().toString()};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 외부에서 사용할 수도 있는 헬퍼 */
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private BigDecimal premiumOrZero(ContractResp c) { return MoneyUtil.zero(c.getPremium()); }
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
package com.ga.batch.settlement.step;
|
||||||
|
|
||||||
|
import com.ga.batch.settlement.SettlementContext;
|
||||||
|
import com.ga.common.util.DateUtil;
|
||||||
|
import com.ga.common.util.MoneyUtil;
|
||||||
|
import com.ga.core.mapper.ledger.RecruitLedgerMapper;
|
||||||
|
import com.ga.core.mapper.product.ContractMapper;
|
||||||
|
import com.ga.core.mapper.rule.RuleMapper;
|
||||||
|
import com.ga.core.mapper.settle.ChargebackMapper;
|
||||||
|
import com.ga.core.vo.product.ContractResp;
|
||||||
|
import com.ga.core.vo.product.ContractSearchParam;
|
||||||
|
import com.ga.core.vo.settle.ChargebackVO;
|
||||||
|
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.math.BigDecimal;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Step 6 — chargebackException: 환수 자동생성 + 예외금액 자동계산
|
||||||
|
*
|
||||||
|
* 환수 대상: 정산월에 LAPSE(실효) 된 계약 — 계약일~실효까지 경과월수로 환수율 결정.
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@JobScope
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ChargebackExceptionStep implements Tasklet {
|
||||||
|
|
||||||
|
private final SettlementContext ctx;
|
||||||
|
private final ContractMapper contractMapper;
|
||||||
|
private final RecruitLedgerMapper recruitMapper;
|
||||||
|
private final RuleMapper ruleMapper;
|
||||||
|
private final ChargebackMapper chargebackMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {
|
||||||
|
log.info("[Step6 chargebackException] settleMonth={}", ctx.getSettleMonth());
|
||||||
|
|
||||||
|
ContractSearchParam param = new ContractSearchParam();
|
||||||
|
param.setStatus("LAPSE");
|
||||||
|
param.setPageSize(Integer.MAX_VALUE);
|
||||||
|
List<ContractResp> lapsed = contractMapper.selectList(param);
|
||||||
|
|
||||||
|
List<ChargebackVO> chargebacks = new ArrayList<>();
|
||||||
|
for (ContractResp c : lapsed) {
|
||||||
|
if (c.getLapseDate() == null) continue;
|
||||||
|
String lapseMonth = DateUtil.getSettleMonth(c.getLapseDate());
|
||||||
|
if (!ctx.getSettleMonth().equals(lapseMonth)) continue;
|
||||||
|
if (c.getContractDate() == null) continue;
|
||||||
|
|
||||||
|
int monthsActive = DateUtil.calcMonthDiff(
|
||||||
|
DateUtil.getSettleMonth(c.getContractDate()), lapseMonth);
|
||||||
|
BigDecimal cbRate = ruleMapper.findChargebackRate(
|
||||||
|
c.getCompanyCode(), c.getInsuranceType(), monthsActive, c.getLapseDate());
|
||||||
|
if (cbRate == null || cbRate.signum() <= 0) continue;
|
||||||
|
|
||||||
|
// 모집수수료 합계 × 환수율
|
||||||
|
BigDecimal originalAmount = MoneyUtil.zero(c.getPremium())
|
||||||
|
.multiply(cbRate)
|
||||||
|
.divide(MoneyUtil.HUNDRED, 2, java.math.RoundingMode.HALF_UP);
|
||||||
|
|
||||||
|
ChargebackVO cb = new ChargebackVO();
|
||||||
|
cb.setContractId(c.getContractId());
|
||||||
|
cb.setAgentId(c.getAgentId());
|
||||||
|
cb.setPolicyNo(c.getPolicyNo());
|
||||||
|
cb.setLapseMonth(monthsActive);
|
||||||
|
cb.setCbRate(cbRate);
|
||||||
|
cb.setCbAmount(originalAmount);
|
||||||
|
cb.setRemainAmount(originalAmount);
|
||||||
|
cb.setSettleMonth(ctx.getSettleMonth());
|
||||||
|
cb.setStatus("NEW");
|
||||||
|
chargebacks.add(cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!chargebacks.isEmpty()) {
|
||||||
|
chargebackMapper.insertBatch(chargebacks);
|
||||||
|
}
|
||||||
|
ctx.setChargebackCount(chargebacks.size());
|
||||||
|
log.info("[Step6] chargeback inserted: {}", chargebacks.size());
|
||||||
|
return RepeatStatus.FINISHED;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package com.ga.batch.settlement.step;
|
||||||
|
|
||||||
|
import com.ga.batch.settlement.SettlementContext;
|
||||||
|
import com.ga.batch.settlement.receive.DataReceiver;
|
||||||
|
import com.ga.core.mapper.product.InsuranceCompanyMapper;
|
||||||
|
import com.ga.core.vo.product.InsuranceCompanyVO;
|
||||||
|
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.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Step 1 — receiveData: 보험사별 DataReceiver 호출 → raw_commission_data 적재
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@JobScope
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ReceiveDataStep implements Tasklet {
|
||||||
|
|
||||||
|
private final SettlementContext ctx;
|
||||||
|
private final InsuranceCompanyMapper companyMapper;
|
||||||
|
private final List<DataReceiver> receivers;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {
|
||||||
|
log.info("[Step1 receiveData] settleMonth={}", ctx.getSettleMonth());
|
||||||
|
int total = 0;
|
||||||
|
for (InsuranceCompanyVO c : companyMapper.selectActive()) {
|
||||||
|
for (DataReceiver r : receivers) {
|
||||||
|
if (r.supports(c.getCompanyCode())) {
|
||||||
|
int n = r.receive(c.getCompanyCode(), ctx.getSettleMonth());
|
||||||
|
log.info(" - {} via {}: {} rows", c.getCompanyCode(), r.getClass().getSimpleName(), n);
|
||||||
|
total += n;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.setReceivedCount(total);
|
||||||
|
log.info("[Step1] received {} rows", total);
|
||||||
|
return RepeatStatus.FINISHED;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package com.ga.batch.settlement.step;
|
||||||
|
|
||||||
|
import com.ga.batch.settlement.SettlementContext;
|
||||||
|
import com.ga.core.mapper.ledger.RecruitLedgerMapper;
|
||||||
|
import com.ga.core.mapper.product.InsuranceCompanyMapper;
|
||||||
|
import com.ga.core.mapper.settle.ReconciliationMapper;
|
||||||
|
import com.ga.core.vo.product.InsuranceCompanyVO;
|
||||||
|
import com.ga.core.vo.settle.ReconciliationVO;
|
||||||
|
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.math.BigDecimal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Step 3 — reconcile: 보험사 통보액 vs 시스템 계산액 대사
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@JobScope
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ReconcileStep implements Tasklet {
|
||||||
|
|
||||||
|
private final SettlementContext ctx;
|
||||||
|
private final InsuranceCompanyMapper companyMapper;
|
||||||
|
private final RecruitLedgerMapper recruitMapper;
|
||||||
|
private final ReconciliationMapper reconMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {
|
||||||
|
log.info("[Step3 reconcile] settleMonth={}", ctx.getSettleMonth());
|
||||||
|
int diff = 0;
|
||||||
|
for (InsuranceCompanyVO c : companyMapper.selectActive()) {
|
||||||
|
BigDecimal systemTotal = recruitMapper.sumByMonth(ctx.getSettleMonth());
|
||||||
|
// 회사 통보액 — 실제로는 raw_commission_data 의 합계로 계산. 여기선 systemTotal 과 동일 처리.
|
||||||
|
BigDecimal companyTotal = systemTotal;
|
||||||
|
|
||||||
|
ReconciliationVO r = new ReconciliationVO();
|
||||||
|
r.setCompanyCode(c.getCompanyCode());
|
||||||
|
r.setSettleMonth(ctx.getSettleMonth());
|
||||||
|
r.setCompanyTotal(companyTotal);
|
||||||
|
r.setSystemTotal(systemTotal);
|
||||||
|
r.setDiffAmount(companyTotal.subtract(systemTotal));
|
||||||
|
r.setDiffCount(0);
|
||||||
|
r.setStatus(r.getDiffAmount().signum() == 0 ? "MATCHED" : "DIFF");
|
||||||
|
reconMapper.upsert(r);
|
||||||
|
if ("DIFF".equals(r.getStatus())) diff++;
|
||||||
|
}
|
||||||
|
ctx.setReconcileDiffCount(diff);
|
||||||
|
log.info("[Step3] reconcile diff companies: {}", diff);
|
||||||
|
return RepeatStatus.FINISHED;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package com.ga.batch.settlement.step;
|
||||||
|
|
||||||
|
import com.ga.batch.settlement.SettlementContext;
|
||||||
|
import com.ga.batch.settlement.transform.MappingEngine;
|
||||||
|
import com.ga.core.mapper.receive.ReceiveMapper;
|
||||||
|
import com.ga.core.vo.receive.RawCommissionDataVO;
|
||||||
|
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.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Step 2 — transformData: company_field_mapping + code_mapping 으로 표준 변환 → 원장 적재
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@JobScope
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class TransformDataStep implements Tasklet {
|
||||||
|
|
||||||
|
private final SettlementContext ctx;
|
||||||
|
private final ReceiveMapper receiveMapper;
|
||||||
|
private final MappingEngine mappingEngine;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {
|
||||||
|
log.info("[Step2 transformData] settleMonth={}", ctx.getSettleMonth());
|
||||||
|
List<RawCommissionDataVO> raws = receiveMapper.selectRawList(null, ctx.getSettleMonth(), "PENDING");
|
||||||
|
int ok = 0;
|
||||||
|
for (RawCommissionDataVO raw : raws) {
|
||||||
|
try {
|
||||||
|
Long ledgerId = mappingEngine.transform(raw);
|
||||||
|
receiveMapper.updateRawStatus(raw.getRawId(), "OK", ledgerId, raw.getMappedTable());
|
||||||
|
ok++;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("transform fail rawId={}: {}", raw.getRawId(), e.getMessage());
|
||||||
|
receiveMapper.updateRawStatus(raw.getRawId(), "ERROR", null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.setTransformedCount(ok);
|
||||||
|
log.info("[Step2] transformed {} / {}", ok, raws.size());
|
||||||
|
return RepeatStatus.FINISHED;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.ga.batch.settlement.transform;
|
||||||
|
|
||||||
|
import com.ga.core.vo.receive.RawCommissionDataVO;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 보험사 raw 데이터 → 표준 원장(recruit_ledger / maintain_ledger / exception_ledger) 으로 변환.
|
||||||
|
*
|
||||||
|
* 본 구현은 단순화된 stub. 실제로는:
|
||||||
|
* - company_field_mapping 으로 raw_content(JSON 등) 에서 필드를 추출
|
||||||
|
* - code_mapping 으로 보험사 코드 → 표준 코드 변환
|
||||||
|
* - data_type 에 따라 모집/유지/예외 원장 선택
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class MappingEngine {
|
||||||
|
|
||||||
|
/** 변환 후 ledger_id 반환 (없으면 null) */
|
||||||
|
public Long transform(RawCommissionDataVO raw) {
|
||||||
|
// TODO: 실제 매핑 로직 — company_field_mapping 기반 동적 변환
|
||||||
|
// 현재는 ID 미지정으로 처리 (Step4/5 에서 contract 기반으로 재계산)
|
||||||
|
log.debug("transform raw={}, type={}", raw.getRawId(), raw.getDataType());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: ga-batch
|
||||||
|
profiles:
|
||||||
|
active: local
|
||||||
|
datasource:
|
||||||
|
url: jdbc:postgresql://localhost:5432/ga
|
||||||
|
username: ga
|
||||||
|
password: ga
|
||||||
|
driver-class-name: org.postgresql.Driver
|
||||||
|
flyway:
|
||||||
|
enabled: false # ga-api 가 마이그레이션 담당
|
||||||
|
data:
|
||||||
|
redis:
|
||||||
|
host: localhost
|
||||||
|
port: 6379
|
||||||
|
batch:
|
||||||
|
jdbc:
|
||||||
|
initialize-schema: always
|
||||||
|
job:
|
||||||
|
enabled: false # 자동 실행 비활성. JobLauncher 로 명시 실행
|
||||||
|
|
||||||
|
server:
|
||||||
|
port: 8081
|
||||||
|
|
||||||
|
mybatis:
|
||||||
|
config-location: classpath:mybatis/mybatis-config.xml
|
||||||
|
mapper-locations: classpath*:mapper/**/*.xml
|
||||||
|
type-aliases-package: com.ga.core.vo,com.ga.common.model
|
||||||
|
|
||||||
|
pagehelper:
|
||||||
|
helper-dialect: postgresql
|
||||||
|
reasonable: true
|
||||||
|
|
||||||
|
ga:
|
||||||
|
jwt:
|
||||||
|
secret: ${JWT_SECRET:change-me-please-this-must-be-at-least-256-bits-long-secret-key}
|
||||||
|
encrypt:
|
||||||
|
key: ${ENCRYPT_KEY:0123456789abcdef0123456789abcdef}
|
||||||
|
|
||||||
|
logging:
|
||||||
|
level:
|
||||||
|
com.ga: INFO
|
||||||
|
org.springframework.batch: INFO
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
-- V13: 추가 인덱스 (성능 최적화)
|
||||||
|
|
||||||
|
-- settle_master: 정산월 기준 빈번한 조회
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_sm_month_status ON settle_master(settle_month, status);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_sm_agent_month ON settle_master(agent_id, settle_month);
|
||||||
|
|
||||||
|
-- recruit_ledger / maintain_ledger: 정산월 + 설계사
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_rl_month_agent ON recruit_ledger(settle_month, agent_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_ml_month_agent ON maintain_ledger(settle_month, agent_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_rl_company ON recruit_ledger(company_code, settle_month);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_ml_company ON maintain_ledger(company_code, settle_month);
|
||||||
|
|
||||||
|
-- exception_ledger: 정산월 + 승인상태
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_el_month_approve ON exception_ledger(settle_month, approve_status);
|
||||||
|
|
||||||
|
-- contract: 만기/실효 처리용
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_contract_lapse ON contract(lapse_date) WHERE lapse_date IS NOT NULL;
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_contract_status_agent ON contract(status, agent_id);
|
||||||
|
|
||||||
|
-- chargeback / payment 인덱스 보강
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_cb_month ON chargeback(settle_month, status);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_pay_month ON payment(pay_date, pay_status);
|
||||||
|
|
||||||
|
-- raw / 에러 / 매핑
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_raw_status ON raw_commission_data(parse_status, settle_month);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_pe_company ON parse_error_log(company_code, resolve_status);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_codemap_lookup ON code_mapping(company_code, code_type, source_code) WHERE is_active = 'Y';
|
||||||
|
|
||||||
|
-- 로그 테이블 부분 인덱스 (날짜 검색)
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_dcl_table_recent ON data_change_log(table_name, created_at DESC);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_aal_url_recent ON api_access_log(url, created_at DESC);
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
-- V14: 파티셔닝 (대용량 로그 테이블)
|
||||||
|
--
|
||||||
|
-- 주의: 운영에서 기존 테이블을 파티셔닝하려면 데이터 마이그레이션이 필요.
|
||||||
|
-- 본 마이그레이션은 신규 테이블만 파티셔닝 적용.
|
||||||
|
-- 기존 api_access_log / data_change_log 는 별도 마이그레이션으로 전환 권장.
|
||||||
|
-- 본 V14 는 파티션 자동 생성을 위한 함수 + cron 형태의 helper.
|
||||||
|
|
||||||
|
-- 월별 파티션 테이블을 자동 생성하는 함수 (운영 시 cron 또는 pg_cron 으로 매월 호출)
|
||||||
|
CREATE OR REPLACE FUNCTION create_monthly_partition(
|
||||||
|
base_table TEXT,
|
||||||
|
target_month DATE
|
||||||
|
) RETURNS void AS $$
|
||||||
|
DECLARE
|
||||||
|
partition_name TEXT;
|
||||||
|
start_date DATE;
|
||||||
|
end_date DATE;
|
||||||
|
BEGIN
|
||||||
|
start_date := date_trunc('month', target_month);
|
||||||
|
end_date := start_date + interval '1 month';
|
||||||
|
partition_name := base_table || '_' || to_char(start_date, 'YYYYMM');
|
||||||
|
|
||||||
|
EXECUTE format(
|
||||||
|
'CREATE TABLE IF NOT EXISTS %I PARTITION OF %I FOR VALUES FROM (%L) TO (%L)',
|
||||||
|
partition_name, base_table, start_date, end_date
|
||||||
|
);
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
COMMENT ON FUNCTION create_monthly_partition IS
|
||||||
|
'파티션 테이블에 월별 파티션을 자동 생성. 예: SELECT create_monthly_partition(''api_access_log'', NOW())';
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
-- V15: 통계/리포트용 뷰
|
||||||
|
|
||||||
|
-- 설계사별 월 실적 요약
|
||||||
|
CREATE OR REPLACE VIEW v_agent_monthly_summary AS
|
||||||
|
SELECT
|
||||||
|
sm.agent_id,
|
||||||
|
a.agent_name,
|
||||||
|
o.org_id,
|
||||||
|
o.org_name,
|
||||||
|
g.grade_name,
|
||||||
|
sm.settle_month,
|
||||||
|
sm.recruit_total,
|
||||||
|
sm.maintain_total,
|
||||||
|
sm.exception_plus,
|
||||||
|
sm.exception_minus,
|
||||||
|
sm.override_total,
|
||||||
|
sm.chargeback_total,
|
||||||
|
sm.gross_amount,
|
||||||
|
sm.tax_amount,
|
||||||
|
sm.net_amount,
|
||||||
|
sm.status
|
||||||
|
FROM settle_master sm
|
||||||
|
JOIN agent a ON a.agent_id = sm.agent_id
|
||||||
|
LEFT JOIN organization o ON o.org_id = a.org_id
|
||||||
|
LEFT JOIN grade g ON g.grade_id = a.grade_id;
|
||||||
|
|
||||||
|
-- 조직별 월 정산 합계
|
||||||
|
CREATE OR REPLACE VIEW v_org_settle_summary AS
|
||||||
|
SELECT
|
||||||
|
a.org_id,
|
||||||
|
o.org_name,
|
||||||
|
o.org_type,
|
||||||
|
sm.settle_month,
|
||||||
|
COUNT(sm.settle_id) AS agent_count,
|
||||||
|
COALESCE(SUM(sm.recruit_total), 0) AS recruit_total,
|
||||||
|
COALESCE(SUM(sm.maintain_total), 0) AS maintain_total,
|
||||||
|
COALESCE(SUM(sm.gross_amount), 0) AS gross_amount,
|
||||||
|
COALESCE(SUM(sm.tax_amount), 0) AS tax_amount,
|
||||||
|
COALESCE(SUM(sm.net_amount), 0) AS net_amount
|
||||||
|
FROM settle_master sm
|
||||||
|
JOIN agent a ON a.agent_id = sm.agent_id
|
||||||
|
JOIN organization o ON o.org_id = a.org_id
|
||||||
|
GROUP BY a.org_id, o.org_name, o.org_type, sm.settle_month;
|
||||||
|
|
||||||
|
-- 환수 위험 계약 (실효 + 환수 발생)
|
||||||
|
CREATE OR REPLACE VIEW v_chargeback_risk AS
|
||||||
|
SELECT
|
||||||
|
cb.cb_id,
|
||||||
|
cb.contract_id,
|
||||||
|
cb.agent_id,
|
||||||
|
a.agent_name,
|
||||||
|
o.org_name,
|
||||||
|
cb.policy_no,
|
||||||
|
cb.lapse_month,
|
||||||
|
cb.cb_rate,
|
||||||
|
cb.cb_amount,
|
||||||
|
cb.paid_amount,
|
||||||
|
cb.remain_amount,
|
||||||
|
cb.settle_month,
|
||||||
|
cb.status
|
||||||
|
FROM chargeback cb
|
||||||
|
JOIN agent a ON a.agent_id = cb.agent_id
|
||||||
|
LEFT JOIN organization o ON o.org_id = a.org_id;
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.ga.core.mapper.ledger;
|
||||||
|
|
||||||
|
import com.ga.core.vo.ledger.ExceptionLedgerResp;
|
||||||
|
import com.ga.core.vo.ledger.ExceptionLedgerVO;
|
||||||
|
import com.ga.core.vo.ledger.LedgerSearchParam;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface ExceptionLedgerMapper {
|
||||||
|
List<ExceptionLedgerResp> selectList(LedgerSearchParam param);
|
||||||
|
ExceptionLedgerResp selectDetailById(@Param("ledgerId") Long ledgerId);
|
||||||
|
ExceptionLedgerVO selectById(@Param("ledgerId") Long ledgerId);
|
||||||
|
int insert(ExceptionLedgerVO vo);
|
||||||
|
int update(ExceptionLedgerVO vo);
|
||||||
|
int updateApprove(@Param("ledgerId") Long ledgerId,
|
||||||
|
@Param("status") String status,
|
||||||
|
@Param("approverId") Long approverId);
|
||||||
|
int decrementRecurring(@Param("ledgerId") Long ledgerId);
|
||||||
|
|
||||||
|
/** 정산월별 PLUS/MINUS 합계 */
|
||||||
|
List<Map<String, Object>> aggregateByAgent(@Param("settleMonth") String settleMonth);
|
||||||
|
BigDecimal sumPlusByMonth(@Param("settleMonth") String settleMonth);
|
||||||
|
BigDecimal sumMinusByMonth(@Param("settleMonth") String settleMonth);
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.ga.core.mapper.ledger;
|
||||||
|
|
||||||
|
import com.ga.core.vo.ledger.LedgerExcelVO;
|
||||||
|
import com.ga.core.vo.ledger.LedgerResp;
|
||||||
|
import com.ga.core.vo.ledger.LedgerSearchParam;
|
||||||
|
import com.ga.core.vo.ledger.LedgerVO;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.apache.ibatis.cursor.Cursor;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface MaintainLedgerMapper {
|
||||||
|
List<LedgerResp> selectList(LedgerSearchParam param);
|
||||||
|
LedgerResp selectDetailById(@Param("ledgerId") Long ledgerId);
|
||||||
|
LedgerVO selectById(@Param("ledgerId") Long ledgerId);
|
||||||
|
Cursor<LedgerExcelVO> selectCursorForExcel(LedgerSearchParam param);
|
||||||
|
int insert(LedgerVO vo);
|
||||||
|
int batchInsert(@Param("list") List<LedgerVO> list);
|
||||||
|
int update(LedgerVO vo);
|
||||||
|
int deleteBySettleMonth(@Param("settleMonth") String settleMonth);
|
||||||
|
List<Map<String, Object>> aggregateByAgent(@Param("settleMonth") String settleMonth);
|
||||||
|
BigDecimal sumByMonth(@Param("settleMonth") String settleMonth);
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package com.ga.core.mapper.ledger;
|
||||||
|
|
||||||
|
import com.ga.core.vo.ledger.LedgerExcelVO;
|
||||||
|
import com.ga.core.vo.ledger.LedgerResp;
|
||||||
|
import com.ga.core.vo.ledger.LedgerSearchParam;
|
||||||
|
import com.ga.core.vo.ledger.LedgerVO;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.apache.ibatis.cursor.Cursor;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface RecruitLedgerMapper {
|
||||||
|
List<LedgerResp> selectList(LedgerSearchParam param);
|
||||||
|
LedgerResp selectDetailById(@Param("ledgerId") Long ledgerId);
|
||||||
|
LedgerVO selectById(@Param("ledgerId") Long ledgerId);
|
||||||
|
Cursor<LedgerExcelVO> selectCursorForExcel(LedgerSearchParam param);
|
||||||
|
int insert(LedgerVO vo);
|
||||||
|
int batchInsert(@Param("list") List<LedgerVO> list);
|
||||||
|
int update(LedgerVO vo);
|
||||||
|
int deleteBySettleMonth(@Param("settleMonth") String settleMonth);
|
||||||
|
|
||||||
|
/** 정산월 기준 설계사별 합계 (정산 마스터용) */
|
||||||
|
List<Map<String, Object>> aggregateByAgent(@Param("settleMonth") String settleMonth);
|
||||||
|
/** 정산월 합계 */
|
||||||
|
BigDecimal sumByMonth(@Param("settleMonth") String settleMonth);
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.ga.core.mapper.org;
|
||||||
|
|
||||||
|
import com.ga.core.vo.org.AgentExcelVO;
|
||||||
|
import com.ga.core.vo.org.AgentOrgHistoryVO;
|
||||||
|
import com.ga.core.vo.org.AgentResp;
|
||||||
|
import com.ga.core.vo.org.AgentSearchParam;
|
||||||
|
import com.ga.core.vo.org.AgentVO;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.apache.ibatis.cursor.Cursor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface AgentMapper {
|
||||||
|
AgentResp selectDetailById(@Param("agentId") Long agentId);
|
||||||
|
AgentVO selectById(@Param("agentId") Long agentId);
|
||||||
|
List<AgentResp> selectList(AgentSearchParam param);
|
||||||
|
Cursor<AgentExcelVO> selectCursorForExcel(AgentSearchParam param);
|
||||||
|
|
||||||
|
int existsByLicenseNo(@Param("licenseNo") String licenseNo);
|
||||||
|
int insert(AgentVO vo);
|
||||||
|
int update(AgentVO vo);
|
||||||
|
int updateStatus(@Param("agentId") Long agentId, @Param("status") String status);
|
||||||
|
int updateOrgGrade(@Param("agentId") Long agentId,
|
||||||
|
@Param("orgId") Long orgId,
|
||||||
|
@Param("gradeId") Long gradeId);
|
||||||
|
|
||||||
|
/** 이력 */
|
||||||
|
List<AgentOrgHistoryVO> selectHistory(@Param("agentId") Long agentId);
|
||||||
|
int insertHistory(AgentOrgHistoryVO vo);
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.ga.core.mapper.org;
|
||||||
|
|
||||||
|
import com.ga.core.vo.org.GradeVO;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface GradeMapper {
|
||||||
|
List<GradeVO> selectAll();
|
||||||
|
List<GradeVO> selectActive();
|
||||||
|
GradeVO selectById(@Param("gradeId") Long gradeId);
|
||||||
|
int insert(GradeVO vo);
|
||||||
|
int update(GradeVO vo);
|
||||||
|
int deleteById(@Param("gradeId") Long gradeId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.ga.core.mapper.org;
|
||||||
|
|
||||||
|
import com.ga.core.vo.org.OrganizationResp;
|
||||||
|
import com.ga.core.vo.org.OrganizationVO;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface OrganizationMapper {
|
||||||
|
List<OrganizationResp> selectAllForTree();
|
||||||
|
List<OrganizationResp> selectList(@Param("keyword") String keyword,
|
||||||
|
@Param("orgType") String orgType,
|
||||||
|
@Param("isActive") String isActive);
|
||||||
|
OrganizationVO selectById(@Param("orgId") Long orgId);
|
||||||
|
/** 자기 자신 + 모든 하위 조직 ID (CTE 재귀) */
|
||||||
|
List<Long> selectDescendantIds(@Param("orgId") Long orgId);
|
||||||
|
int insert(OrganizationVO vo);
|
||||||
|
int update(OrganizationVO vo);
|
||||||
|
int deleteById(@Param("orgId") Long orgId);
|
||||||
|
int countChildren(@Param("orgId") Long orgId);
|
||||||
|
int countAgents(@Param("orgId") Long orgId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.ga.core.mapper.product;
|
||||||
|
|
||||||
|
import com.ga.core.vo.product.ContractResp;
|
||||||
|
import com.ga.core.vo.product.ContractSearchParam;
|
||||||
|
import com.ga.core.vo.product.ContractVO;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface ContractMapper {
|
||||||
|
List<ContractResp> selectList(ContractSearchParam param);
|
||||||
|
ContractResp selectDetailById(@Param("contractId") Long contractId);
|
||||||
|
ContractVO selectById(@Param("contractId") Long contractId);
|
||||||
|
int existsByPolicyNo(@Param("policyNo") String policyNo);
|
||||||
|
int insert(ContractVO vo);
|
||||||
|
int update(ContractVO vo);
|
||||||
|
int updateStatus(@Param("contractId") Long contractId, @Param("status") String status);
|
||||||
|
int countByAgent(@Param("agentId") Long agentId, @Param("status") String status);
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.ga.core.mapper.product;
|
||||||
|
|
||||||
|
import com.ga.core.vo.product.InsuranceCompanyVO;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface InsuranceCompanyMapper {
|
||||||
|
List<InsuranceCompanyVO> selectAll();
|
||||||
|
List<InsuranceCompanyVO> selectActive();
|
||||||
|
InsuranceCompanyVO selectById(@Param("companyId") Long companyId);
|
||||||
|
InsuranceCompanyVO selectByCode(@Param("companyCode") String companyCode);
|
||||||
|
int insert(InsuranceCompanyVO vo);
|
||||||
|
int update(InsuranceCompanyVO vo);
|
||||||
|
int deleteById(@Param("companyId") Long companyId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.ga.core.mapper.product;
|
||||||
|
|
||||||
|
import com.ga.core.vo.product.ProductResp;
|
||||||
|
import com.ga.core.vo.product.ProductVO;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface ProductMapper {
|
||||||
|
List<ProductResp> selectList(@Param("companyId") Long companyId,
|
||||||
|
@Param("insuranceType") String insuranceType,
|
||||||
|
@Param("keyword") String keyword,
|
||||||
|
@Param("isActive") String isActive);
|
||||||
|
ProductResp selectDetailById(@Param("productId") Long productId);
|
||||||
|
ProductVO selectById(@Param("productId") Long productId);
|
||||||
|
int existsByCode(@Param("companyId") Long companyId, @Param("productCode") String productCode);
|
||||||
|
int insert(ProductVO vo);
|
||||||
|
int update(ProductVO vo);
|
||||||
|
int deleteById(@Param("productId") Long productId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package com.ga.core.mapper.receive;
|
||||||
|
|
||||||
|
import com.ga.core.vo.receive.*;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface ReceiveMapper {
|
||||||
|
|
||||||
|
// company_profile
|
||||||
|
List<CompanyProfileVO> selectAllProfiles();
|
||||||
|
CompanyProfileVO selectProfile(@Param("companyCode") String companyCode);
|
||||||
|
int insertProfile(CompanyProfileVO vo);
|
||||||
|
int updateProfile(CompanyProfileVO vo);
|
||||||
|
|
||||||
|
// field_mapping
|
||||||
|
List<CompanyFieldMappingVO> selectFieldMappings(@Param("companyCode") String companyCode,
|
||||||
|
@Param("targetTable") String targetTable);
|
||||||
|
int insertFieldMapping(CompanyFieldMappingVO vo);
|
||||||
|
int updateFieldMapping(CompanyFieldMappingVO vo);
|
||||||
|
int deleteFieldMapping(@Param("mappingId") Long mappingId);
|
||||||
|
|
||||||
|
// code_mapping
|
||||||
|
List<CodeMappingVO> selectCodeMappings(@Param("companyCode") String companyCode,
|
||||||
|
@Param("codeType") String codeType);
|
||||||
|
String findTargetCode(@Param("companyCode") String companyCode,
|
||||||
|
@Param("codeType") String codeType,
|
||||||
|
@Param("sourceCode") String sourceCode);
|
||||||
|
int insertCodeMapping(CodeMappingVO vo);
|
||||||
|
int updateCodeMapping(CodeMappingVO vo);
|
||||||
|
int deleteCodeMapping(@Param("codeId") Long codeId);
|
||||||
|
|
||||||
|
// raw_commission_data
|
||||||
|
int insertRaw(RawCommissionDataVO vo);
|
||||||
|
int insertRawBatch(@Param("list") List<RawCommissionDataVO> list);
|
||||||
|
List<RawCommissionDataVO> selectRawList(@Param("companyCode") String companyCode,
|
||||||
|
@Param("settleMonth") String settleMonth,
|
||||||
|
@Param("parseStatus") String parseStatus);
|
||||||
|
int updateRawStatus(@Param("rawId") Long rawId,
|
||||||
|
@Param("parseStatus") String parseStatus,
|
||||||
|
@Param("mappedLedgerId") Long mappedLedgerId,
|
||||||
|
@Param("mappedTable") String mappedTable);
|
||||||
|
|
||||||
|
// parse_error_log
|
||||||
|
List<ParseErrorLogVO> selectErrors(@Param("companyCode") String companyCode,
|
||||||
|
@Param("resolveStatus") String resolveStatus);
|
||||||
|
int insertError(ParseErrorLogVO vo);
|
||||||
|
int resolveError(@Param("errorId") Long errorId,
|
||||||
|
@Param("status") String status,
|
||||||
|
@Param("note") String note,
|
||||||
|
@Param("userId") Long userId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package com.ga.core.mapper.rule;
|
||||||
|
|
||||||
|
import com.ga.core.vo.rule.*;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 수수료 규정 Mapper. 5개 테이블을 통합한 인터페이스.
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface RuleMapper {
|
||||||
|
|
||||||
|
// === commission_rate ===
|
||||||
|
List<CommissionRateVO> selectCommissionRates(@Param("productId") Long productId,
|
||||||
|
@Param("year") Integer year);
|
||||||
|
/** 계약일에 유효한 보험사 수수료율 */
|
||||||
|
BigDecimal findCompanyRate(@Param("productId") Long productId,
|
||||||
|
@Param("year") Integer year,
|
||||||
|
@Param("baseDate") LocalDate baseDate);
|
||||||
|
int insertCommissionRate(CommissionRateVO vo);
|
||||||
|
int updateCommissionRate(CommissionRateVO vo);
|
||||||
|
int deleteCommissionRate(@Param("rateId") Long rateId);
|
||||||
|
|
||||||
|
// === payout_rule ===
|
||||||
|
List<PayoutRuleVO> selectPayoutRules(@Param("gradeId") Long gradeId,
|
||||||
|
@Param("insuranceType") String insuranceType);
|
||||||
|
BigDecimal findPayoutRate(@Param("gradeId") Long gradeId,
|
||||||
|
@Param("insuranceType") String insuranceType,
|
||||||
|
@Param("year") Integer year,
|
||||||
|
@Param("baseDate") LocalDate baseDate);
|
||||||
|
int insertPayoutRule(PayoutRuleVO vo);
|
||||||
|
int updatePayoutRule(PayoutRuleVO vo);
|
||||||
|
int deletePayoutRule(@Param("ruleId") Long ruleId);
|
||||||
|
|
||||||
|
// === override_rule ===
|
||||||
|
List<OverrideRuleVO> selectOverrideRules();
|
||||||
|
int insertOverrideRule(OverrideRuleVO vo);
|
||||||
|
int updateOverrideRule(OverrideRuleVO vo);
|
||||||
|
int deleteOverrideRule(@Param("overrideId") Long overrideId);
|
||||||
|
|
||||||
|
// === chargeback_rule ===
|
||||||
|
List<ChargebackRuleVO> selectChargebackRules(@Param("companyCode") String companyCode);
|
||||||
|
BigDecimal findChargebackRate(@Param("companyCode") String companyCode,
|
||||||
|
@Param("insuranceType") String insuranceType,
|
||||||
|
@Param("lapseMonth") Integer lapseMonth,
|
||||||
|
@Param("baseDate") LocalDate baseDate);
|
||||||
|
int insertChargebackRule(ChargebackRuleVO vo);
|
||||||
|
int updateChargebackRule(ChargebackRuleVO vo);
|
||||||
|
int deleteChargebackRule(@Param("cbRuleId") Long cbRuleId);
|
||||||
|
|
||||||
|
// === exception_type_code ===
|
||||||
|
List<ExceptionTypeCodeVO> selectExceptionCodes();
|
||||||
|
ExceptionTypeCodeVO selectExceptionCode(@Param("exceptionCode") String exceptionCode);
|
||||||
|
int insertExceptionCode(ExceptionTypeCodeVO vo);
|
||||||
|
int updateExceptionCode(ExceptionTypeCodeVO vo);
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.ga.core.mapper.settle;
|
||||||
|
|
||||||
|
import com.ga.core.vo.settle.BatchJobLogVO;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface BatchJobLogMapper {
|
||||||
|
int insert(BatchJobLogVO vo);
|
||||||
|
int update(BatchJobLogVO vo);
|
||||||
|
BatchJobLogVO selectById(@Param("jobId") Long jobId);
|
||||||
|
BatchJobLogVO selectLatest(@Param("jobName") String jobName);
|
||||||
|
List<BatchJobLogVO> selectList(@Param("jobName") String jobName,
|
||||||
|
@Param("settleMonth") String settleMonth);
|
||||||
|
int isRunning(@Param("jobName") String jobName);
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.ga.core.mapper.settle;
|
||||||
|
|
||||||
|
import com.ga.core.vo.settle.ChargebackVO;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface ChargebackMapper {
|
||||||
|
int insert(ChargebackVO vo);
|
||||||
|
int insertBatch(@Param("list") List<ChargebackVO> list);
|
||||||
|
List<ChargebackVO> selectByAgent(@Param("agentId") Long agentId,
|
||||||
|
@Param("settleMonth") String settleMonth);
|
||||||
|
int updatePaid(@Param("cbId") Long cbId,
|
||||||
|
@Param("paidAmount") BigDecimal paidAmount,
|
||||||
|
@Param("status") String status);
|
||||||
|
|
||||||
|
List<Map<String, Object>> aggregateByAgent(@Param("settleMonth") String settleMonth);
|
||||||
|
BigDecimal sumByMonth(@Param("settleMonth") String settleMonth);
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.ga.core.mapper.settle;
|
||||||
|
|
||||||
|
import com.ga.core.vo.settle.OverrideSettleVO;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface OverrideSettleMapper {
|
||||||
|
int insert(OverrideSettleVO vo);
|
||||||
|
int insertBatch(@Param("list") List<OverrideSettleVO> list);
|
||||||
|
List<OverrideSettleVO> selectByUpper(@Param("upperAgentId") Long upperAgentId,
|
||||||
|
@Param("settleMonth") String settleMonth);
|
||||||
|
int deleteBySettleMonth(@Param("settleMonth") String settleMonth);
|
||||||
|
/** 정산월 + 상위설계사별 override 합계 */
|
||||||
|
List<Map<String, Object>> aggregateByUpper(@Param("settleMonth") String settleMonth);
|
||||||
|
BigDecimal sumByMonth(@Param("settleMonth") String settleMonth);
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.ga.core.mapper.settle;
|
||||||
|
|
||||||
|
import com.ga.core.vo.settle.PaymentResp;
|
||||||
|
import com.ga.core.vo.settle.PaymentVO;
|
||||||
|
import com.ga.core.vo.settle.SettleSearchParam;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface PaymentMapper {
|
||||||
|
List<PaymentResp> selectList(SettleSearchParam param);
|
||||||
|
PaymentVO selectById(@Param("paymentId") Long paymentId);
|
||||||
|
int insert(PaymentVO vo);
|
||||||
|
int insertBatch(@Param("list") List<PaymentVO> list);
|
||||||
|
int updateStatus(@Param("paymentId") Long paymentId,
|
||||||
|
@Param("status") String status,
|
||||||
|
@Param("failReason") String failReason);
|
||||||
|
int updateTransferFile(@Param("paymentIds") List<Long> paymentIds,
|
||||||
|
@Param("transferFileId") Long transferFileId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.ga.core.mapper.settle;
|
||||||
|
|
||||||
|
import com.ga.core.vo.settle.ReconciliationVO;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface ReconciliationMapper {
|
||||||
|
List<ReconciliationVO> selectByMonth(@Param("settleMonth") String settleMonth);
|
||||||
|
int upsert(ReconciliationVO vo);
|
||||||
|
int resolve(@Param("reconId") Long reconId,
|
||||||
|
@Param("note") String note,
|
||||||
|
@Param("userId") Long userId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package com.ga.core.mapper.settle;
|
||||||
|
|
||||||
|
import com.ga.core.vo.settle.SettleMasterResp;
|
||||||
|
import com.ga.core.vo.settle.SettleMasterVO;
|
||||||
|
import com.ga.core.vo.settle.SettleSearchParam;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface SettleMasterMapper {
|
||||||
|
List<SettleMasterResp> selectList(SettleSearchParam param);
|
||||||
|
SettleMasterResp selectDetail(@Param("agentId") Long agentId,
|
||||||
|
@Param("settleMonth") String settleMonth);
|
||||||
|
SettleMasterVO selectByAgentAndMonth(@Param("agentId") Long agentId,
|
||||||
|
@Param("settleMonth") String settleMonth);
|
||||||
|
SettleMasterVO selectById(@Param("settleId") Long settleId);
|
||||||
|
|
||||||
|
/** 정산 마스터 UPSERT (agent_id + settle_month UNIQUE) */
|
||||||
|
int upsert(SettleMasterVO vo);
|
||||||
|
|
||||||
|
int updateStatus(@Param("settleId") Long settleId,
|
||||||
|
@Param("status") String status,
|
||||||
|
@Param("userId") Long userId);
|
||||||
|
int updateHold(@Param("settleId") Long settleId,
|
||||||
|
@Param("reason") String reason);
|
||||||
|
|
||||||
|
/** 월 요약 (전체) */
|
||||||
|
Map<String, Object> selectMonthSummary(@Param("settleMonth") String settleMonth);
|
||||||
|
|
||||||
|
/** 조직별 월 합계 */
|
||||||
|
List<Map<String, Object>> selectOrgSummary(@Param("settleMonth") String settleMonth);
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.ga.core.mapper.user;
|
||||||
|
|
||||||
|
import com.ga.core.vo.user.RoleVO;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface RoleMapper {
|
||||||
|
List<RoleVO> selectAll();
|
||||||
|
RoleVO selectById(@Param("roleId") Long roleId);
|
||||||
|
int insert(RoleVO vo);
|
||||||
|
int update(RoleVO vo);
|
||||||
|
int deleteById(@Param("roleId") Long roleId);
|
||||||
|
|
||||||
|
/** 역할의 (menu_id, perm_code) 권한 목록 */
|
||||||
|
List<java.util.Map<String, Object>> selectRolePermissions(@Param("roleId") Long roleId);
|
||||||
|
int deleteRolePermissions(@Param("roleId") Long roleId);
|
||||||
|
int insertRolePermission(@Param("roleId") Long roleId,
|
||||||
|
@Param("menuId") Long menuId,
|
||||||
|
@Param("permCode") String permCode,
|
||||||
|
@Param("isGranted") String isGranted);
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.ga.core.mapper.user;
|
||||||
|
|
||||||
|
import com.ga.core.vo.user.UserResp;
|
||||||
|
import com.ga.core.vo.user.UserSearchParam;
|
||||||
|
import com.ga.core.vo.user.UserVO;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface UserMapper {
|
||||||
|
|
||||||
|
UserVO selectById(@Param("userId") Long userId);
|
||||||
|
UserVO selectByLoginId(@Param("loginId") String loginId);
|
||||||
|
UserResp selectDetailById(@Param("userId") Long userId);
|
||||||
|
|
||||||
|
List<UserResp> selectList(UserSearchParam param);
|
||||||
|
int countByCondition(UserSearchParam param);
|
||||||
|
|
||||||
|
int insert(UserVO vo);
|
||||||
|
int update(UserVO vo);
|
||||||
|
int updatePassword(@Param("userId") Long userId, @Param("password") String password);
|
||||||
|
int updateLastLogin(@Param("userId") Long userId);
|
||||||
|
int incrementFailCount(@Param("userId") Long userId);
|
||||||
|
int resetFailCount(@Param("userId") Long userId);
|
||||||
|
int updateStatus(@Param("userId") Long userId, @Param("status") String status);
|
||||||
|
|
||||||
|
List<String> selectRoleCodes(@Param("userId") Long userId);
|
||||||
|
|
||||||
|
int insertUserRole(@Param("userId") Long userId, @Param("roleId") Long roleId);
|
||||||
|
int deleteUserRoles(@Param("userId") Long userId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.ga.core.vo.ledger;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ExceptionLedgerResp {
|
||||||
|
private Long ledgerId;
|
||||||
|
private Long contractId;
|
||||||
|
private Long agentId;
|
||||||
|
private String agentName;
|
||||||
|
private String orgName;
|
||||||
|
private String exceptionCode;
|
||||||
|
private String exceptionName;
|
||||||
|
private String direction;
|
||||||
|
private String policyNo;
|
||||||
|
private String companyCode;
|
||||||
|
private String insuranceType;
|
||||||
|
private BigDecimal amount;
|
||||||
|
private BigDecimal taxAmount;
|
||||||
|
private String settleMonth;
|
||||||
|
private String description;
|
||||||
|
private String approveStatus;
|
||||||
|
private String approveStatusName;
|
||||||
|
private Long approverId;
|
||||||
|
private String approverName;
|
||||||
|
private LocalDateTime approveDate;
|
||||||
|
private String recurringYn;
|
||||||
|
private Integer recurringLeft;
|
||||||
|
private String status;
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.ga.core.vo.ledger;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ExceptionLedgerSaveReq {
|
||||||
|
@NotNull private Long agentId;
|
||||||
|
@NotBlank private String exceptionCode;
|
||||||
|
@NotBlank private String settleMonth;
|
||||||
|
@NotNull private BigDecimal amount;
|
||||||
|
private String policyNo;
|
||||||
|
private String companyCode;
|
||||||
|
private String insuranceType;
|
||||||
|
private String description;
|
||||||
|
private String recurringYn;
|
||||||
|
private Integer recurringMonths;
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.ga.core.vo.ledger;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ExceptionLedgerVO {
|
||||||
|
private Long ledgerId;
|
||||||
|
private Long contractId;
|
||||||
|
private Long agentId;
|
||||||
|
private String exceptionCode;
|
||||||
|
private String policyNo;
|
||||||
|
private String companyCode;
|
||||||
|
private String insuranceType;
|
||||||
|
private Integer commissionYear;
|
||||||
|
private BigDecimal amount;
|
||||||
|
private BigDecimal taxAmount;
|
||||||
|
private String settleMonth;
|
||||||
|
private String description;
|
||||||
|
private String approveStatus; // PENDING / APPROVED / REJECTED
|
||||||
|
private Long approverId;
|
||||||
|
private LocalDateTime approveDate;
|
||||||
|
private String recurringYn;
|
||||||
|
private Integer recurringMonths;
|
||||||
|
private Integer recurringLeft;
|
||||||
|
private String status;
|
||||||
|
private Integer version;
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
private LocalDateTime updatedAt;
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package com.ga.core.vo.ledger;
|
||||||
|
|
||||||
|
import com.ga.common.annotation.ExcelColumn;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class LedgerExcelVO {
|
||||||
|
@ExcelColumn(header = "정산월", order = 1, width = 10)
|
||||||
|
private String settleMonth;
|
||||||
|
@ExcelColumn(header = "증권번호", order = 2, width = 18)
|
||||||
|
private String policyNo;
|
||||||
|
@ExcelColumn(header = "설계사명", order = 3, width = 12)
|
||||||
|
private String agentName;
|
||||||
|
@ExcelColumn(header = "소속", order = 4, width = 18)
|
||||||
|
private String orgName;
|
||||||
|
@ExcelColumn(header = "보험사", order = 5, width = 14)
|
||||||
|
private String companyName;
|
||||||
|
@ExcelColumn(header = "상품", order = 6, width = 24)
|
||||||
|
private String productName;
|
||||||
|
@ExcelColumn(header = "보험종류", order = 7, width = 10, codeGroup = "INSURANCE_TYPE")
|
||||||
|
private String insuranceType;
|
||||||
|
@ExcelColumn(header = "보험료", order = 8, width = 14, format = "#,##0")
|
||||||
|
private BigDecimal premium;
|
||||||
|
@ExcelColumn(header = "회사수수료율", order = 9, width = 12, format = "0.00")
|
||||||
|
private BigDecimal companyRate;
|
||||||
|
@ExcelColumn(header = "회사수수료", order = 10, width = 14, format = "#,##0")
|
||||||
|
private BigDecimal companyAmount;
|
||||||
|
@ExcelColumn(header = "지급율", order = 11, width = 10, format = "0.00")
|
||||||
|
private BigDecimal payoutRate;
|
||||||
|
@ExcelColumn(header = "설계사수수료", order = 12, width = 14, format = "#,##0")
|
||||||
|
private BigDecimal agentAmount;
|
||||||
|
@ExcelColumn(header = "세액", order = 13, width = 12, format = "#,##0")
|
||||||
|
private BigDecimal taxAmount;
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package com.ga.core.vo.ledger;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class LedgerResp {
|
||||||
|
private Long ledgerId;
|
||||||
|
private Long contractId;
|
||||||
|
private Long agentId;
|
||||||
|
private String agentName;
|
||||||
|
private String orgName;
|
||||||
|
private String policyNo;
|
||||||
|
private String contractorName;
|
||||||
|
private LocalDate contractDate;
|
||||||
|
private String companyCode;
|
||||||
|
private String companyName;
|
||||||
|
private String productCode;
|
||||||
|
private String productName;
|
||||||
|
private String insuranceType;
|
||||||
|
private String insuranceTypeName;
|
||||||
|
private Integer commissionYear;
|
||||||
|
private BigDecimal premium;
|
||||||
|
private String payMethod;
|
||||||
|
private BigDecimal companyRate;
|
||||||
|
private BigDecimal companyAmount;
|
||||||
|
private BigDecimal payoutRate;
|
||||||
|
private BigDecimal agentAmount;
|
||||||
|
private BigDecimal taxAmount;
|
||||||
|
private String reconcileStatus;
|
||||||
|
private BigDecimal reconcileDiff;
|
||||||
|
private String advanceYn;
|
||||||
|
private String chargebackYn;
|
||||||
|
private String settleMonth;
|
||||||
|
private String status;
|
||||||
|
private String statusName;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.ga.core.vo.ledger;
|
||||||
|
|
||||||
|
import com.ga.common.model.SearchParam;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class LedgerSearchParam extends SearchParam {
|
||||||
|
private String settleMonth;
|
||||||
|
private Long agentId;
|
||||||
|
private Long orgId;
|
||||||
|
private String companyCode;
|
||||||
|
private String insuranceType;
|
||||||
|
private String reconcileStatus;
|
||||||
|
private String chargebackYn;
|
||||||
|
private String advanceYn;
|
||||||
|
private String policyNo;
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package com.ga.core.vo.ledger;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* recruit_ledger / maintain_ledger 공통 VO. 두 테이블 구조가 동일하므로 같은 VO 사용.
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class LedgerVO {
|
||||||
|
private Long ledgerId;
|
||||||
|
private Long contractId;
|
||||||
|
private Long agentId;
|
||||||
|
private String policyNo;
|
||||||
|
private String companyCode;
|
||||||
|
private String productCode;
|
||||||
|
private String insuranceType;
|
||||||
|
private Integer commissionYear;
|
||||||
|
private BigDecimal premium;
|
||||||
|
private String payMethod;
|
||||||
|
private BigDecimal companyRate;
|
||||||
|
private BigDecimal companyAmount;
|
||||||
|
private BigDecimal payoutRate;
|
||||||
|
private BigDecimal agentAmount;
|
||||||
|
private BigDecimal taxAmount;
|
||||||
|
private String reconcileStatus;
|
||||||
|
private BigDecimal reconcileDiff;
|
||||||
|
private String advanceYn;
|
||||||
|
private BigDecimal advanceAmount;
|
||||||
|
private String chargebackYn;
|
||||||
|
private String settleMonth;
|
||||||
|
private String status;
|
||||||
|
private Integer version;
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
private LocalDateTime updatedAt;
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package com.ga.core.vo.org;
|
||||||
|
|
||||||
|
import com.ga.common.annotation.ExcelColumn;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class AgentExcelVO {
|
||||||
|
@ExcelColumn(header = "설계사명", order = 1, width = 12, required = true)
|
||||||
|
private String agentName;
|
||||||
|
|
||||||
|
@ExcelColumn(header = "사번", order = 2, width = 14)
|
||||||
|
private String licenseNo;
|
||||||
|
|
||||||
|
@ExcelColumn(header = "소속", order = 3, width = 18)
|
||||||
|
private String orgName;
|
||||||
|
|
||||||
|
@ExcelColumn(header = "직급", order = 4, width = 10)
|
||||||
|
private String gradeName;
|
||||||
|
|
||||||
|
@ExcelColumn(header = "전화번호", order = 5, width = 16)
|
||||||
|
private String phone;
|
||||||
|
|
||||||
|
@ExcelColumn(header = "이메일", order = 6, width = 24)
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
@ExcelColumn(header = "주민번호", order = 7, width = 16, masked = true)
|
||||||
|
private String residentNo;
|
||||||
|
|
||||||
|
@ExcelColumn(header = "상태", order = 8, width = 10, codeGroup = "AGENT_STATUS")
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
@ExcelColumn(header = "입사일", order = 9, width = 12, format = "yyyy-MM-dd")
|
||||||
|
private LocalDate joinDate;
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.ga.core.vo.org;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class AgentOrgHistoryVO {
|
||||||
|
private Long historyId;
|
||||||
|
private Long agentId;
|
||||||
|
private Long fromOrgId;
|
||||||
|
private Long toOrgId;
|
||||||
|
private Long fromGradeId;
|
||||||
|
private Long toGradeId;
|
||||||
|
private String changeType; // TRANSFER / PROMOTE / DEMOTE / JOIN / LEAVE
|
||||||
|
private LocalDate changeDate;
|
||||||
|
private String changeReason;
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
private Long createdBy;
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.ga.core.vo.org;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class AgentResp {
|
||||||
|
private Long agentId;
|
||||||
|
private String agentName;
|
||||||
|
private String licenseNo;
|
||||||
|
private String phone; // 마스킹
|
||||||
|
private String email;
|
||||||
|
private String status;
|
||||||
|
private String statusName;
|
||||||
|
private Long orgId;
|
||||||
|
private String orgName;
|
||||||
|
private Long gradeId;
|
||||||
|
private String gradeName;
|
||||||
|
private LocalDate joinDate;
|
||||||
|
private LocalDate leaveDate;
|
||||||
|
private Integer contractCount;
|
||||||
|
private BigDecimal totalCommission;
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.ga.core.vo.org;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class AgentSaveReq {
|
||||||
|
@NotBlank(message = "설계사명은 필수입니다")
|
||||||
|
@Size(max = 50)
|
||||||
|
private String agentName;
|
||||||
|
|
||||||
|
@NotNull(message = "소속을 선택해주세요")
|
||||||
|
private Long orgId;
|
||||||
|
|
||||||
|
@NotNull(message = "직급을 선택해주세요")
|
||||||
|
private Long gradeId;
|
||||||
|
|
||||||
|
private String residentNo;
|
||||||
|
private String licenseNo;
|
||||||
|
private String phone;
|
||||||
|
private String email;
|
||||||
|
private String bankCode;
|
||||||
|
private String accountNo;
|
||||||
|
private LocalDate joinDate;
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.ga.core.vo.org;
|
||||||
|
|
||||||
|
import com.ga.common.model.SearchParam;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class AgentSearchParam extends SearchParam {
|
||||||
|
private Long orgId;
|
||||||
|
private Long gradeId;
|
||||||
|
private Boolean includeSubOrgs; // 하위 조직 포함 여부
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.ga.core.vo.org;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class AgentVO {
|
||||||
|
private Long agentId;
|
||||||
|
private Long orgId;
|
||||||
|
private Long gradeId;
|
||||||
|
private String agentName;
|
||||||
|
private String residentNo; // EncryptTypeHandler
|
||||||
|
private String licenseNo;
|
||||||
|
private String phone;
|
||||||
|
private String email;
|
||||||
|
private String bankCode;
|
||||||
|
private String accountNo; // EncryptTypeHandler
|
||||||
|
private String status;
|
||||||
|
private LocalDate joinDate;
|
||||||
|
private LocalDate leaveDate;
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
private LocalDateTime updatedAt;
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.ga.core.vo.org;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class GradeVO {
|
||||||
|
private Long gradeId;
|
||||||
|
private String gradeName;
|
||||||
|
private Integer gradeLevel;
|
||||||
|
private String gradeGroup;
|
||||||
|
private String description;
|
||||||
|
private String isActive;
|
||||||
|
private Integer sortOrder;
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.ga.core.vo.org;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class OrganizationResp {
|
||||||
|
private Long orgId;
|
||||||
|
private Long parentOrgId;
|
||||||
|
private String parentOrgName;
|
||||||
|
private String orgName;
|
||||||
|
private String orgType;
|
||||||
|
private String orgTypeName;
|
||||||
|
private Integer orgLevel;
|
||||||
|
private String region;
|
||||||
|
private LocalDate effectiveFrom;
|
||||||
|
private LocalDate effectiveTo;
|
||||||
|
private String isActive;
|
||||||
|
private Integer agentCount;
|
||||||
|
private List<OrganizationResp> children = new ArrayList<>();
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.ga.core.vo.org;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class OrganizationVO {
|
||||||
|
private Long orgId;
|
||||||
|
private Long parentOrgId;
|
||||||
|
private String orgName;
|
||||||
|
private String orgType; // HQ / BR / TM
|
||||||
|
private Integer orgLevel;
|
||||||
|
private String region;
|
||||||
|
private LocalDate effectiveFrom;
|
||||||
|
private LocalDate effectiveTo;
|
||||||
|
private String isActive;
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
private LocalDateTime updatedAt;
|
||||||
|
private Long createdBy;
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.ga.core.vo.product;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ContractResp {
|
||||||
|
private Long contractId;
|
||||||
|
private Long agentId;
|
||||||
|
private String agentName;
|
||||||
|
private String orgName;
|
||||||
|
private Long productId;
|
||||||
|
private String productCode;
|
||||||
|
private String productName;
|
||||||
|
private String companyCode;
|
||||||
|
private String companyName;
|
||||||
|
private String insuranceType;
|
||||||
|
private String policyNo;
|
||||||
|
private String contractorName;
|
||||||
|
private String insuredName;
|
||||||
|
private BigDecimal premium;
|
||||||
|
private String payCycle;
|
||||||
|
private String payCycleName;
|
||||||
|
private Integer payPeriod;
|
||||||
|
private Integer coveragePeriod;
|
||||||
|
private String status;
|
||||||
|
private String statusName;
|
||||||
|
private LocalDate contractDate;
|
||||||
|
private LocalDate effectiveDate;
|
||||||
|
private LocalDate lapseDate;
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.ga.core.vo.product;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import jakarta.validation.constraints.Positive;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ContractSaveReq {
|
||||||
|
@NotNull private Long agentId;
|
||||||
|
@NotNull private Long productId;
|
||||||
|
@NotBlank(message = "증권번호는 필수입니다")
|
||||||
|
private String policyNo;
|
||||||
|
private String contractorName;
|
||||||
|
private String insuredName;
|
||||||
|
@NotNull @Positive
|
||||||
|
private BigDecimal premium;
|
||||||
|
private String payCycle;
|
||||||
|
private Integer payPeriod;
|
||||||
|
private Integer coveragePeriod;
|
||||||
|
@NotNull private LocalDate contractDate;
|
||||||
|
private LocalDate effectiveDate;
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.ga.core.vo.product;
|
||||||
|
|
||||||
|
import com.ga.common.model.SearchParam;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class ContractSearchParam extends SearchParam {
|
||||||
|
private Long agentId;
|
||||||
|
private Long productId;
|
||||||
|
private Long companyId;
|
||||||
|
private String insuranceType;
|
||||||
|
private String policyNo;
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.ga.core.vo.product;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ContractVO {
|
||||||
|
private Long contractId;
|
||||||
|
private Long agentId;
|
||||||
|
private Long productId;
|
||||||
|
private String policyNo;
|
||||||
|
private String contractorName;
|
||||||
|
private String insuredName;
|
||||||
|
private BigDecimal premium;
|
||||||
|
private String payCycle;
|
||||||
|
private Integer payPeriod;
|
||||||
|
private Integer coveragePeriod;
|
||||||
|
private String status;
|
||||||
|
private LocalDate contractDate;
|
||||||
|
private LocalDate effectiveDate;
|
||||||
|
private LocalDate lapseDate;
|
||||||
|
private LocalDate revivalDate;
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
private LocalDateTime updatedAt;
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.ga.core.vo.product;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class InsuranceCompanyVO {
|
||||||
|
private Long companyId;
|
||||||
|
private String companyCode;
|
||||||
|
private String companyName;
|
||||||
|
private String companyType;
|
||||||
|
private String bizNo;
|
||||||
|
private String contactName;
|
||||||
|
private String contactPhone;
|
||||||
|
private String contactEmail;
|
||||||
|
private String isActive;
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.ga.core.vo.product;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ProductResp {
|
||||||
|
private Long productId;
|
||||||
|
private Long companyId;
|
||||||
|
private String companyCode;
|
||||||
|
private String companyName;
|
||||||
|
private String productCode;
|
||||||
|
private String productName;
|
||||||
|
private String insuranceType;
|
||||||
|
private String insuranceTypeName;
|
||||||
|
private String productGroup;
|
||||||
|
private String payPeriodType;
|
||||||
|
private String isActive;
|
||||||
|
private LocalDate launchDate;
|
||||||
|
private LocalDate endDate;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.ga.core.vo.product;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ProductVO {
|
||||||
|
private Long productId;
|
||||||
|
private Long companyId;
|
||||||
|
private String productCode;
|
||||||
|
private String productName;
|
||||||
|
private String insuranceType;
|
||||||
|
private String productGroup;
|
||||||
|
private String payPeriodType;
|
||||||
|
private String isActive;
|
||||||
|
private LocalDate launchDate;
|
||||||
|
private LocalDate endDate;
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.ga.core.vo.receive;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class CodeMappingVO {
|
||||||
|
private Long codeId;
|
||||||
|
private String companyCode;
|
||||||
|
private String codeType;
|
||||||
|
private String sourceCode;
|
||||||
|
private String sourceName;
|
||||||
|
private String targetCode;
|
||||||
|
private String targetName;
|
||||||
|
private String isActive;
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.ga.core.vo.receive;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class CompanyFieldMappingVO {
|
||||||
|
private Long mappingId;
|
||||||
|
private String companyCode;
|
||||||
|
private String sourceField;
|
||||||
|
private Integer sourceColIndex;
|
||||||
|
private String targetTable;
|
||||||
|
private String targetField;
|
||||||
|
private String dataType;
|
||||||
|
private String transformRule;
|
||||||
|
private String defaultValue;
|
||||||
|
private String isRequired;
|
||||||
|
private Integer sortOrder;
|
||||||
|
private Integer version;
|
||||||
|
private LocalDate effectiveFrom;
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.ga.core.vo.receive;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class CompanyProfileVO {
|
||||||
|
private String companyCode;
|
||||||
|
private String dataFormat;
|
||||||
|
private String receiveMethod;
|
||||||
|
private String fileEncoding;
|
||||||
|
private String delimiter;
|
||||||
|
private Integer headerRow;
|
||||||
|
private Integer dataStartRow;
|
||||||
|
private String sheetName;
|
||||||
|
private String dateFormat;
|
||||||
|
private String amountFormat;
|
||||||
|
private String apiUrl;
|
||||||
|
private String ftpHost;
|
||||||
|
private String ftpPath;
|
||||||
|
private String receiveSchedule;
|
||||||
|
private String isActive;
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.ga.core.vo.receive;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ParseErrorLogVO {
|
||||||
|
private Long errorId;
|
||||||
|
private Long rawId;
|
||||||
|
private String companyCode;
|
||||||
|
private String errorType;
|
||||||
|
private String errorField;
|
||||||
|
private String errorValue;
|
||||||
|
private String errorMessage;
|
||||||
|
private String resolveStatus; // OPEN / RESOLVED / IGNORED
|
||||||
|
private Long resolvedBy;
|
||||||
|
private String resolveNote;
|
||||||
|
private LocalDateTime resolvedAt;
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.ga.core.vo.receive;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class RawCommissionDataVO {
|
||||||
|
private Long rawId;
|
||||||
|
private String companyCode;
|
||||||
|
private String batchId;
|
||||||
|
private String settleMonth;
|
||||||
|
private String dataType;
|
||||||
|
private String rawContent;
|
||||||
|
private String parseStatus; // PENDING / OK / ERROR
|
||||||
|
private Long mappedLedgerId;
|
||||||
|
private String mappedTable;
|
||||||
|
private String fileName;
|
||||||
|
private Integer rowNumber;
|
||||||
|
private LocalDateTime receivedAt;
|
||||||
|
private LocalDateTime parsedAt;
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.ga.core.vo.rule;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ChargebackRuleVO {
|
||||||
|
private Long cbRuleId;
|
||||||
|
private String companyCode;
|
||||||
|
private String insuranceType;
|
||||||
|
private Integer lapseMonthFrom;
|
||||||
|
private Integer lapseMonthTo;
|
||||||
|
private BigDecimal cbRate;
|
||||||
|
private String includeOverride;
|
||||||
|
private String installmentAllowed;
|
||||||
|
private Integer maxInstallments;
|
||||||
|
private LocalDate effectiveFrom;
|
||||||
|
private LocalDate effectiveTo;
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.ga.core.vo.rule;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class CommissionRateVO {
|
||||||
|
private Long rateId;
|
||||||
|
private Long productId;
|
||||||
|
private Integer commissionYear;
|
||||||
|
private BigDecimal ratePct;
|
||||||
|
private String payMethodCond;
|
||||||
|
private BigDecimal minPremium;
|
||||||
|
private LocalDate effectiveFrom;
|
||||||
|
private LocalDate effectiveTo;
|
||||||
|
private Integer version;
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
private Long createdBy;
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.ga.core.vo.rule;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ExceptionTypeCodeVO {
|
||||||
|
private String exceptionCode;
|
||||||
|
private String exceptionName;
|
||||||
|
private String category;
|
||||||
|
private String direction; // PLUS / MINUS
|
||||||
|
private String autoCalcYn;
|
||||||
|
private String approvalRequired;
|
||||||
|
private String taxIncluded;
|
||||||
|
private String description;
|
||||||
|
private String isActive;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.ga.core.vo.rule;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class OverrideRuleVO {
|
||||||
|
private Long overrideId;
|
||||||
|
private Long fromGrade;
|
||||||
|
private Long toGrade;
|
||||||
|
private BigDecimal overridePct;
|
||||||
|
private String calcType;
|
||||||
|
private String insuranceTypeCond;
|
||||||
|
private BigDecimal capAmount;
|
||||||
|
private LocalDate effectiveFrom;
|
||||||
|
private LocalDate effectiveTo;
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.ga.core.vo.rule;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class PayoutRuleVO {
|
||||||
|
private Long ruleId;
|
||||||
|
private Long gradeId;
|
||||||
|
private String insuranceType;
|
||||||
|
private Integer commissionYear;
|
||||||
|
private BigDecimal payoutPct;
|
||||||
|
private String payMethodCond;
|
||||||
|
private String performanceGrade;
|
||||||
|
private LocalDate effectiveFrom;
|
||||||
|
private LocalDate effectiveTo;
|
||||||
|
private Integer version;
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
private Long createdBy;
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.ga.core.vo.settle;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class BatchJobLogVO {
|
||||||
|
private Long jobId;
|
||||||
|
private String jobName;
|
||||||
|
private String jobParams;
|
||||||
|
private String settleMonth;
|
||||||
|
private String status;
|
||||||
|
private String currentStep;
|
||||||
|
private Integer progressPct;
|
||||||
|
private Integer totalCount;
|
||||||
|
private Integer processedCount;
|
||||||
|
private Integer errorCount;
|
||||||
|
private String errorMessage;
|
||||||
|
private LocalDateTime startedAt;
|
||||||
|
private LocalDateTime finishedAt;
|
||||||
|
private Integer durationSec;
|
||||||
|
private Long triggeredBy;
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.ga.core.vo.settle;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ChargebackVO {
|
||||||
|
private Long cbId;
|
||||||
|
private Long contractId;
|
||||||
|
private Long agentId;
|
||||||
|
private String policyNo;
|
||||||
|
private Integer lapseMonth;
|
||||||
|
private BigDecimal cbRate;
|
||||||
|
private BigDecimal cbAmount;
|
||||||
|
private BigDecimal paidAmount;
|
||||||
|
private BigDecimal remainAmount;
|
||||||
|
private String settleMonth;
|
||||||
|
private Integer installmentNo;
|
||||||
|
private Integer maxInstallments;
|
||||||
|
private String status;
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user