동기화

This commit is contained in:
GA Pro
2026-05-15 00:25:18 +09:00
parent 88e14edffd
commit d5ed642c80
69 changed files with 2634 additions and 26 deletions
@@ -0,0 +1,15 @@
package com.ga.api.controller.accounting;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
import java.util.List;
@Data
public class CloseJournalReq {
@NotEmpty
private List<Long> entryIds;
@NotBlank
private String journalNo;
}
@@ -0,0 +1,47 @@
package com.ga.api.controller.accounting;
import com.ga.api.service.accounting.ContractAccountingService;
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.accounting.ContractAccountingEntryResp;
import com.ga.core.vo.accounting.ContractAccountingEntrySearchParam;
import io.swagger.v3.oas.annotations.Operation;
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/accounting-entries")
@RequiredArgsConstructor
public class ContractAccountingController {
private final ContractAccountingService service;
@GetMapping
@Operation(summary = "분개 엔트리 목록 조회")
@RequirePermission(menu = "ACCOUNTING_JOURNAL", perm = "READ")
public ApiResponse<PageResponse<ContractAccountingEntryResp>> list(
@ModelAttribute ContractAccountingEntrySearchParam param) {
return ApiResponse.ok(service.list(param));
}
@PostMapping("/generate")
@Operation(summary = "자동 분개 생성 (paymentIds 또는 settleMonth)")
@RequirePermission(menu = "ACCOUNTING_JOURNAL", perm = "CREATE")
@DataChangeLog(menu = "ACCOUNTING_JOURNAL", table = "contract_accounting_entry")
public ApiResponse<Integer> generate(@RequestBody GenerateEntriesReq req) {
return ApiResponse.ok(service.generateFromPayments(req));
}
@PutMapping("/close")
@Operation(summary = "분개 마감 처리")
@RequirePermission(menu = "ACCOUNTING_JOURNAL", perm = "UPDATE")
@DataChangeLog(menu = "ACCOUNTING_JOURNAL", table = "contract_accounting_entry")
public ApiResponse<Integer> close(@Valid @RequestBody CloseJournalReq req) {
return ApiResponse.ok(service.closeJournal(req));
}
}
@@ -0,0 +1,11 @@
package com.ga.api.controller.accounting;
import lombok.Data;
import java.util.List;
@Data
public class GenerateEntriesReq {
private List<Long> paymentIds;
private String settleMonth;
}
@@ -0,0 +1,47 @@
package com.ga.api.controller.income;
import com.ga.api.service.income.AgentAnnualIncomeService;
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.income.AgentAnnualIncomeResp;
import com.ga.core.vo.income.AgentAnnualIncomeSearchParam;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
@Tag(name = "연간 사업소득")
@RestController
@RequestMapping("/api/agent-annual-incomes")
@RequiredArgsConstructor
public class AgentAnnualIncomeController {
private final AgentAnnualIncomeService service;
@GetMapping
@Operation(summary = "연간소득 목록 조회")
@RequirePermission(menu = "ANNUAL_INCOME", perm = "READ")
public ApiResponse<PageResponse<AgentAnnualIncomeResp>> list(
@ModelAttribute AgentAnnualIncomeSearchParam param) {
return ApiResponse.ok(service.list(param));
}
@GetMapping("/{agentId}/{settleYear}")
@Operation(summary = "연간소득 상세 조회")
@RequirePermission(menu = "ANNUAL_INCOME", perm = "READ")
public ApiResponse<AgentAnnualIncomeResp> detail(
@PathVariable Long agentId,
@PathVariable int settleYear) {
return ApiResponse.ok(service.detail(agentId, settleYear));
}
@PostMapping("/regenerate")
@Operation(summary = "연도 전체 재집계")
@RequirePermission(menu = "ANNUAL_INCOME", perm = "EXECUTE")
@DataChangeLog(menu = "ANNUAL_INCOME", table = "agent_annual_income")
public ApiResponse<Integer> regenerate(@RequestParam int year) {
return ApiResponse.ok(service.regenerate(year));
}
}
@@ -63,6 +63,14 @@ public class RegulatoryLimitController {
return ApiResponse.ok();
}
@PutMapping("/{limitId}/activate")
@RequirePermission(menu = "REGULATORY_LIMIT", perm = "UPDATE")
@DataChangeLog(menu = "REGULATORY_LIMIT", table = "regulatory_limit")
public ApiResponse<Void> activate(@PathVariable Long limitId) {
limitService.activate(limitId);
return ApiResponse.ok();
}
@PostMapping("/check-contract")
@RequirePermission(menu = "REGULATORY_LIMIT", perm = "READ")
public ApiResponse<ViolationResult> checkContract(@Valid @RequestBody ContractLimitCheckReq req) {
@@ -0,0 +1,83 @@
package com.ga.api.service.accounting;
import com.ga.api.controller.accounting.CloseJournalReq;
import com.ga.api.controller.accounting.GenerateEntriesReq;
import com.ga.common.exception.BizException;
import com.ga.common.exception.ErrorCode;
import com.ga.common.model.PageResponse;
import com.ga.core.mapper.accounting.ContractAccountingEntryMapper;
import com.ga.core.mapper.settle.PaymentMapper;
import com.ga.core.vo.accounting.ContractAccountingEntryResp;
import com.ga.core.vo.accounting.ContractAccountingEntrySearchParam;
import com.ga.core.vo.accounting.ContractAccountingEntryVO;
import com.ga.core.vo.settle.PaymentVO;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class ContractAccountingService {
private static final String DEBIT_ACCOUNT = "8350"; // 지급수수료
private static final String CREDIT_ACCOUNT = "2530"; // 미지급금
private final ContractAccountingEntryMapper entryMapper;
private final PaymentMapper paymentMapper;
public PageResponse<ContractAccountingEntryResp> list(ContractAccountingEntrySearchParam param) {
param.startPage();
return PageResponse.of(entryMapper.selectList(param));
}
@Transactional
public int generateFromPayments(GenerateEntriesReq req) {
List<PaymentVO> payments = resolvePayments(req);
if (payments.isEmpty()) throw new BizException(ErrorCode.NOT_FOUND);
List<ContractAccountingEntryVO> entries = new ArrayList<>(payments.size());
for (PaymentVO p : payments) {
entries.add(buildEntry(p));
}
return entryMapper.insertBatch(entries);
}
@Transactional
public int closeJournal(CloseJournalReq req) {
if (req.getEntryIds() == null || req.getEntryIds().isEmpty()) {
throw new BizException(ErrorCode.INVALID_PARAMETER);
}
return entryMapper.closeJournal(req.getEntryIds(), req.getJournalNo());
}
private List<PaymentVO> resolvePayments(GenerateEntriesReq req) {
if (req.getPaymentIds() != null && !req.getPaymentIds().isEmpty()) {
List<PaymentVO> result = new ArrayList<>();
for (Long id : req.getPaymentIds()) {
PaymentVO p = paymentMapper.selectById(id);
if (p != null) result.add(p);
}
return result;
}
if (req.getSettleMonth() != null && !req.getSettleMonth().isBlank()) {
return paymentMapper.selectBySettleMonth(req.getSettleMonth());
}
return List.of();
}
private ContractAccountingEntryVO buildEntry(PaymentVO p) {
ContractAccountingEntryVO vo = new ContractAccountingEntryVO();
vo.setPaymentId(p.getPaymentId());
vo.setEntryDate(LocalDate.now());
vo.setDebitAccount(DEBIT_ACCOUNT);
vo.setCreditAccount(CREDIT_ACCOUNT);
vo.setAmount(p.getNetAmount() != null ? p.getNetAmount() : p.getPayAmount());
vo.setDescription("commission payment " + p.getPaymentId());
return vo;
}
}
@@ -0,0 +1,35 @@
package com.ga.api.service.income;
import com.ga.common.exception.BizException;
import com.ga.common.exception.ErrorCode;
import com.ga.common.model.PageResponse;
import com.ga.core.mapper.income.AgentAnnualIncomeMapper;
import com.ga.core.vo.income.AgentAnnualIncomeResp;
import com.ga.core.vo.income.AgentAnnualIncomeSearchParam;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class AgentAnnualIncomeService {
private final AgentAnnualIncomeMapper mapper;
public PageResponse<AgentAnnualIncomeResp> list(AgentAnnualIncomeSearchParam param) {
param.startPage();
return PageResponse.of(mapper.selectList(param));
}
public AgentAnnualIncomeResp detail(Long agentId, int settleYear) {
AgentAnnualIncomeResp resp = mapper.selectDetailById(agentId, settleYear);
if (resp == null) throw new BizException(ErrorCode.NOT_FOUND);
return resp;
}
@Transactional
public int regenerate(int year) {
return mapper.upsertAggregate(year);
}
}
@@ -79,6 +79,24 @@ public class RegulatoryLimitService {
limitMapper.updateStatus(limitId, false);
}
@Transactional
public void activate(Long limitId) {
RegulatoryLimitVO existing = limitMapper.selectById(limitId);
if (existing == null) {
throw new BizException(ErrorCode.NOT_FOUND);
}
if (Boolean.TRUE.equals(existing.getIsActive())) {
throw new BizException(ErrorCode.INVALID_PARAMETER);
}
String today = LocalDate.now().format(DATE_FMT);
RegulatoryLimitVO duplicate = limitMapper.selectActiveByType(
existing.getLimitType(), existing.getProductCategory(), today);
if (duplicate != null) {
throw new BizException(ErrorCode.DUPLICATE_DATA);
}
limitMapper.updateStatus(limitId, true);
}
@Transactional(readOnly = true)
public RegulatoryLimitVO selectActiveByType(String limitType, String productCategory, String baseDate) {
return limitMapper.selectActiveByType(limitType, productCategory, baseDate);
@@ -4,7 +4,7 @@ ga:
spring:
datasource:
url: jdbc:postgresql://125.241.236.203:55432/ga
url: jdbc:postgresql://192.168.0.60:55432/ga
username: ga
password: ga
cache:
@@ -3,7 +3,7 @@
spring:
datasource:
url: jdbc:postgresql://125.241.236.203:55432/trading_ai?currentSchema=ga
url: jdbc:postgresql://192.168.0.60:55432/trading_ai?currentSchema=ga
username: ${DB_USER:kyu}
password: ${DB_PASSWORD:7895123}
driver-class-name: org.postgresql.Driver