과금 컨트롤러 추가

This commit is contained in:
현성필
2025-11-16 21:58:55 +09:00
parent 65f038340a
commit 5af11f8d55
10 changed files with 588 additions and 0 deletions
@@ -0,0 +1,30 @@
package com.eactive.apim.portal.apps.commission.constant;
/**
* Application constants.
*/
public final class Constants {
public static final String SYSTEM_ACCOUNT = "system";
public static final String APIM_FOR_OBP_KEY = "x-obp-trust-system";
public static final String APIM_FOR_OBP_VALUE = "APIMPT-0002-QVBJTVBULTAwMDI=";
public static final String APIM_CODE_FOR_OBP_KEY = "x-obp-partnercode";
public static final String APIM_CODE_FOR_OBP_VALUE = "100001-01";
public static final String SMS_URL = "/api/messaging/sms";
public static final String COMMISSION_URL = "/api/billing/billing/findBillingForCondition";
public static final String COMMISSION_PRINT_URL = "/api/billing/billing/findBillingForCondition/print";
public static final String OBP_ORGANIZATION_URL = "/api/customer/findCustomerByBusinessManRegistrationNo/";
public static String LANGUAGE = "ko";
private Constants() {
}
}
@@ -0,0 +1,271 @@
package com.eactive.apim.portal.apps.commission.controller;
import com.eactive.apim.portal.apps.commission.constant.Constants;
import com.eactive.apim.portal.apps.commission.dto.CommissionDTO;
import com.eactive.apim.portal.apps.commission.dto.CommissionSearch;
import com.eactive.apim.portal.apps.commission.exception.ErrorUtil;
import com.eactive.apim.portal.apps.commission.service.ExternalService;
import com.eactive.apim.portal.common.util.SecurityUtil;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping(value = "/commission")
@Secured("ROLE_APP")
@RequiredArgsConstructor
public class CommissionController {
private final Logger log = LoggerFactory.getLogger(CommissionController.class);
private final ExternalService externalService;
@Value("${obm-url:http://localhost:8080}")
private String obmUrl;
@Value("${provider-code:100001-01}")
private String providerCode;
// ==================== View Controllers ====================
@GetMapping("/manage")
public String commissionManagePage(Model model) {
// 기본 정보 설정
model.addAttribute("organization", SecurityUtil.getUserOrg());
return "apps/commission/commissionManage";
}
@GetMapping("/print/{year}/{month}")
public String commissionPrintPage(
@PathVariable("year") String year,
@PathVariable("month") String month,
Model model) {
CommissionDTO commissionDTO = null;
CommissionSearch search = new CommissionSearch();
search.setPartnerCode(SecurityUtil.getUserOrg().getOrgCode());
search.setYear(year);
search.setMonth(month);
search.setProvider(providerCode);
if (!StringUtils.isEmpty(search.getPartnerCode())) {
try {
String url = obmUrl + Constants.COMMISSION_URL;
String result = externalService.getResponseHttpPost(url, toJson(search));
HashMap<String, Object> returnObj = new ObjectMapper().readValue(result, HashMap.class);
if (!StringUtils.isEmpty(returnObj.get("error"))) {
HashMap<String, Object> error = (HashMap) returnObj.get("error");
model.addAttribute("error", error.get("message").toString());
return "apps/commission/commissionPrint";
}
if (StringUtils.pathEquals(search.getPartnerCode(), "000002-01")) {
log.debug("Found Together : " + search.getPartnerCode());
search.setPartnerCode("000002-02");
String togetherResult = externalService.getResponseHttpPost(url, toJson(search));
HashMap<String, Object> togetherLendingReturnObj = new ObjectMapper().readValue(togetherResult, HashMap.class);
log.debug("togetherLendingReturnObj : " + togetherLendingReturnObj.toString());
if (!StringUtils.isEmpty(togetherLendingReturnObj.get("error"))) {
HashMap<String, Object> error = (HashMap) togetherLendingReturnObj.get("error");
model.addAttribute("error", error.get("message").toString());
return "apps/commission/commissionPrint";
}
HashMap<String, Object> mergeMap = mergeCommissionData(returnObj, togetherLendingReturnObj);
result = new ObjectMapper().writeValueAsString(mergeMap);
}
commissionDTO = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).readValue(result, CommissionDTO.class);
} catch (Exception e) {
log.warn(e.getMessage(), e);
model.addAttribute("error", "수수료 조회에 실패하였습니다.");
return "apps/commission/commissionPrint";
}
}
// Model에 데이터 바인딩
model.addAttribute("commission", commissionDTO);
model.addAttribute("organization", SecurityUtil.getUserOrg().getOrgName());
model.addAttribute("year", year);
model.addAttribute("month", month);
model.addAttribute("toDay", java.time.LocalDate.now().toString());
return "apps/commission/commissionPrint";
}
// ==================== API Endpoints ====================
@PostMapping()
public ResponseEntity<?> list(@RequestBody CommissionSearch search) {
CommissionDTO commissionDTO = null;
search.setPartnerCode(SecurityUtil.getUserOrg().getOrgCode());
if (!StringUtils.isEmpty(search.getPartnerCode())) {
try {
String url = obmUrl + Constants.COMMISSION_URL;
String result = externalService.getResponseHttpPost(url, toJson(search));
HashMap<String, Object> returnObj = new ObjectMapper().readValue(result, HashMap.class);
if (!StringUtils.isEmpty(returnObj.get("error"))) {
HashMap<String, Object> error = (HashMap) returnObj.get("error");
return ErrorUtil.create("fail.commission.serverError", error.get("message").toString());
}
if (StringUtils.pathEquals(search.getPartnerCode(), "000002-01")) {
log.debug("Found Together : " + search.getPartnerCode());
search.setPartnerCode("000002-02");
String togetherResult = externalService.getResponseHttpPost(url, toJson(search));
HashMap<String, Object> togetherLendingReturnObj = new ObjectMapper().readValue(togetherResult, HashMap.class);
log.debug("togetherLendingReturnObj : " + togetherLendingReturnObj.toString());
if (!StringUtils.isEmpty(togetherLendingReturnObj.get("error"))) {
HashMap<String, Object> error = (HashMap) togetherLendingReturnObj.get("error");
return ErrorUtil.create("fail.commission.serverError", error.get("message").toString());
}
HashMap<String, Object> mergeMap = mergeCommissionData(returnObj, togetherLendingReturnObj);
result = new ObjectMapper().writeValueAsString(mergeMap);
}
commissionDTO = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).readValue(result, CommissionDTO.class);
} catch (Exception e) {
log.warn(e.getMessage(), e);
return ErrorUtil.create("fail.commission.list", "수수료 조회에 실패하였습니다.");
}
return ResponseEntity.ok().body(commissionDTO);
}
return ErrorUtil.create("fail.commission.needPartnerCode");
}
@GetMapping("/print1/{year}/{month}")
public ResponseEntity<?> print(
@PathVariable("year") String year,
@PathVariable("month") String month) {
CommissionDTO commissionDTO = null;
CommissionSearch search = new CommissionSearch();
search.setOrganization(SecurityUtil.getPortalAuthenticatedUser().getPortalOrg());
search.setProvider(providerCode);
search.setYear(year);
search.setMonth(month);
search.setPartnerCode(SecurityUtil.getUserOrg().getOrgCode());
if (!StringUtils.isEmpty(search.getPartnerCode())) {
try {
String url = obmUrl + Constants.COMMISSION_PRINT_URL;
String result = externalService.getResponseHttpPost(url, toJson(search));
HashMap<String, Object> returnObj = new ObjectMapper().readValue(result, HashMap.class);
if (StringUtils.pathEquals(search.getPartnerCode(), "000002-01")) {
log.debug("Found Together : " + search.getPartnerCode());
search.setPartnerCode("000002-02");
String togetherResult = externalService.getResponseHttpPost(url, toJson(search));
HashMap<String, Object> togetherLendingReturnObj = new ObjectMapper().readValue(togetherResult, HashMap.class);
log.debug("togetherLendingReturnObj : " + togetherLendingReturnObj.toString());
if (!StringUtils.isEmpty(togetherLendingReturnObj.get("error"))) {
HashMap<String, Object> error = (HashMap) togetherLendingReturnObj.get("error");
return ErrorUtil.create("fail.commission.serverError", error.get("message").toString());
}
HashMap<String, Object> mergeMap = mergeCommissionData(returnObj, togetherLendingReturnObj);
result = new ObjectMapper().writeValueAsString(mergeMap);
}
commissionDTO = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).readValue(result, CommissionDTO.class);
} catch (Exception e) {
log.warn(e.getMessage(), e);
return ErrorUtil.create("fail.commission.list", "수수료 조회에 실패하였습니다.");
}
return ResponseEntity.ok().body(commissionDTO);
}
return ErrorUtil.create("fail.commission.needPartnerCode");
}
private String toJson(CommissionSearch search) {
try {
Map<String, String> jsonMap = new HashMap<>();
jsonMap.put("partnerCode", search.getPartnerCode());
jsonMap.put("targetOnMonth", search.getYear() + search.getMonth());
return new ObjectMapper().writeValueAsString(jsonMap);
} catch (Exception e) {
log.warn("Failed to serialize CommissionSearch to JSON", e);
return "{}";
}
}
public HashMap<String, Object> mergeCommissionData(HashMap<String, Object> o1, HashMap<String, Object> o2) {
HashMap<String, Object> mergeMap = new HashMap<String, Object>();
String o1Status = MapUtils.getString(o1, "status");
String o2Status = MapUtils.getString(o2, "status");
String resultStatus = null;
if (Integer.parseInt(o1Status) <= Integer.parseInt(o2Status)) {
resultStatus = o1Status;
} else {
resultStatus = o2Status;
}
double apiTotalValueSum = MapUtils.getDouble(o1, "apiTotalValueSum") + MapUtils.getDouble(o2, "apiTotalValueSum");
double apiSum = MapUtils.getDouble(o1, "apiSum") + MapUtils.getDouble(o2, "apiSum");
double apiTotalValueSum2 = MapUtils.getDouble(o1, "apiTotalValueSum2") + MapUtils.getDouble(o2, "apiTotalValueSum2");
double apiSum2 = MapUtils.getDouble(o1, "apiSum2") + MapUtils.getDouble(o2, "apiSum2");
double productTotalValueCountSum = MapUtils.getDouble(o1, "productTotalValueCountSum") + MapUtils.getDouble(o2, "productTotalValueCountSum");
double productTotalValueAmountSum = MapUtils.getDouble(o1, "productTotalValueAmountSum") + MapUtils.getDouble(o2, "productTotalValueAmountSum");
double productSum = MapUtils.getDouble(o1, "productSum") + MapUtils.getDouble(o2, "productSum");
double productTotalValueCountSum2 = MapUtils.getDouble(o1, "productTotalValueCountSum2") + MapUtils.getDouble(o2, "productTotalValueCountSum2");
double productTotalValueAmountSum2 = MapUtils.getDouble(o1, "productTotalValueAmountSum2") + MapUtils.getDouble(o2, "productTotalValueAmountSum2");
double productSum2 = MapUtils.getDouble(o1, "productSum2") + MapUtils.getDouble(o2, "productSum2");
double totalValueCountSum = MapUtils.getDouble(o1, "totalValueCountSum") + MapUtils.getDouble(o2, "totalValueCountSum");
double totalValueCountSum2 = MapUtils.getDouble(o1, "totalValueCountSum2") + MapUtils.getDouble(o2, "totalValueCountSum2");
double totalValueAmountSum = MapUtils.getDouble(o1, "totalValueAmountSum") + MapUtils.getDouble(o2, "totalValueAmountSum");
double totalValueAmountSum2 = MapUtils.getDouble(o1, "totalValueAmountSum2") + MapUtils.getDouble(o2, "totalValueAmountSum2");
double sum = MapUtils.getDouble(o1, "sum") + MapUtils.getDouble(o2, "sum");
double sum2 = MapUtils.getDouble(o1, "sum2") + MapUtils.getDouble(o2, "sum2");
List<Map> o1List = (ArrayList) o1.get("list");
List<Map> o2pList = (ArrayList) o2.get("list");
o1List.addAll(o2pList);
mergeMap.put("status", resultStatus);
mergeMap.put("apiTotalValueSum", String.format("%.2f", apiTotalValueSum));
mergeMap.put("apiSum", String.format("%.2f", apiSum));
mergeMap.put("apiTotalValueSum2", String.format("%.2f", apiTotalValueSum2));
mergeMap.put("apiSum2", String.format("%.2f", apiSum2));
mergeMap.put("productTotalValueCountSum", String.format("%.2f", productTotalValueCountSum));
mergeMap.put("productTotalValueAmountSum", String.format("%.2f", productTotalValueAmountSum));
mergeMap.put("productSum", String.format("%.2f", productSum));
mergeMap.put("productTotalValueCountSum2", String.format("%.2f", productTotalValueCountSum2));
mergeMap.put("productTotalValueAmountSum2", String.format("%.2f", productTotalValueAmountSum2));
mergeMap.put("productSum2", String.format("%.2f", productSum2));
mergeMap.put("totalValueCountSum", String.format("%.2f", totalValueCountSum));
mergeMap.put("totalValueCountSum2", String.format("%.2f", totalValueCountSum2));
mergeMap.put("totalValueAmountSum", String.format("%.2f", totalValueAmountSum));
mergeMap.put("totalValueAmountSum2", String.format("%.2f", totalValueAmountSum2));
mergeMap.put("sum", String.format("%.2f", sum));
mergeMap.put("sum2", String.format("%.2f", sum2));
mergeMap.put("list", o1List);
return mergeMap;
}
}
@@ -0,0 +1,28 @@
package com.eactive.apim.portal.apps.commission.dto;
import java.util.List;
import lombok.Data;
@Data
public class CommissionDTO {
private String status;
private String apiTotalValueSum;
private String apiSum;
private String apiTotalValueSum2;
private String apiSum2;
private String productTotalValueCountSum;
private String productTotalValueAmountSum;
private String productSum;
private String productTotalValueCountSum2;
private String productTotalValueAmountSum2;
private String productSum2;
private String totalValueCountSum;
private String totalValueCountSum2;
private String totalValueAmountSum;
private String totalValueAmountSum2;
private String sum;
private String sum2;
private List<CommissionList> list;
private String documentFormatNo;
}
@@ -0,0 +1,32 @@
package com.eactive.apim.portal.apps.commission.dto;
import lombok.Data;
/**
* Created by jskim on 17. 01. 26.
*
* @author jskim
*/
@Data
public class CommissionList {
private String paymentTargetName;
private String paymentTargetType;
private String value;
private String minimumAmount;
private String maximumAmount;
private String totalCount;
private String totalCount2;
private String totalValue;
private String totalValue2;
private String minimumAmount2;
private String maximumAmount2;
private String paymentBaseName;
private String paymentTimingName;
private String valueDivisionName;
private String sum;
private String sum2;
private String detailFeeTypeName;
private String value2;
private String changeReason2;
}
@@ -0,0 +1,21 @@
package com.eactive.apim.portal.apps.commission.dto;
import com.eactive.apim.portal.portalorg.entity.PortalOrg;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class CommissionSearch {
@ApiModelProperty(value = "대상년")
private String year;
@ApiModelProperty(value = "대상월")
private String month;
private String partnerCode;
private PortalOrg organization;
private String provider;
}
@@ -0,0 +1,27 @@
package com.eactive.apim.portal.apps.commission.exception;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
/**
* Created by ybsong on 16. 12. 7.
*
* @author ybsong
*/
public class ErrorUtil {
public static ResponseEntity<ErrorVM> create(HttpStatus status, String message, String description) {
return new ResponseEntity<>(new ErrorVM(message, description), status);
}
public static ResponseEntity<ErrorVM> create(HttpStatus status, String message) {
return create(status, message, null);
}
public static ResponseEntity<ErrorVM> create(String message, String description) {
return create(HttpStatus.BAD_REQUEST, message, description);
}
public static ResponseEntity<ErrorVM> create(String message) {
return create(message, null);
}
}
@@ -0,0 +1,43 @@
package com.eactive.apim.portal.apps.commission.exception;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import lombok.Data;
/**
* View Model for transferring error message with a list of field errors.
*/
@Data
public class ErrorVM implements Serializable {
private static final long serialVersionUID = 1L;
private final String message;
private final String description;
private List<FieldErrorVM> fieldErrors;
public ErrorVM(String message) {
this(message, null);
}
public ErrorVM(String message, String description) {
this.message = message;
this.description = description;
}
public ErrorVM(String message, String description, List<FieldErrorVM> fieldErrors) {
this.message = message;
this.description = description;
this.fieldErrors = fieldErrors;
}
public void add(String objectName, String field, String message) {
if (fieldErrors == null) {
fieldErrors = new ArrayList<>();
}
fieldErrors.add(new FieldErrorVM(objectName, field, message));
}
}
@@ -0,0 +1,33 @@
package com.eactive.apim.portal.apps.commission.exception;
import java.io.Serializable;
public class FieldErrorVM implements Serializable {
private static final long serialVersionUID = 1L;
private final String objectName;
private final String field;
private final String message;
public FieldErrorVM(String dto, String field, String message) {
this.objectName = dto;
this.field = field;
this.message = message;
}
public String getObjectName() {
return objectName;
}
public String getField() {
return field;
}
public String getMessage() {
return message;
}
}
@@ -0,0 +1,68 @@
package com.eactive.apim.portal.apps.commission.service;
import com.eactive.apim.portal.apps.commission.constant.Constants;
import javax.inject.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
/**
* 외부 API 호출 서비스
* RestTemplate 기반으로 외부 시스템과 HTTP 통신을 수행합니다.
*
* @author ybsong
* @since 2017-03-02
*/
@Service
@Transactional
public class ExternalService {
private final Logger log = LoggerFactory.getLogger(ExternalService.class);
private final RestTemplate restTemplate;
@Inject
public ExternalService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
/**
* HTTP POST 요청을 통해 외부 API를 호출하고 응답을 받습니다.
*
* @param url 호출할 API URL
* @param payload 요청 본문 (JSON 문자열)
* @return API 응답 본문 (문자열), 오류 발생 시 null
*/
public String getResponseHttpPost(String url, String payload) {
try {
// HTTP 헤더 설정
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set(Constants.APIM_FOR_OBP_KEY, Constants.APIM_FOR_OBP_VALUE);
headers.set(Constants.APIM_CODE_FOR_OBP_KEY, Constants.APIM_CODE_FOR_OBP_VALUE);
// HTTP 요청 엔티티 생성 (헤더 + 본문)
HttpEntity<String> requestEntity = new HttpEntity<>(payload, headers);
// POST 요청 실행
ResponseEntity<String> response = restTemplate.postForEntity(url, requestEntity, String.class);
// 응답 본문 반환
return response.getBody();
} catch (RestClientException e) {
log.error("Failed to call external API: url={}, error={}", url, e.getMessage(), e);
return null;
} catch (Exception e) {
log.error("Unexpected error during HTTP POST: url={}", url, e);
return null;
}
}
}
@@ -0,0 +1,35 @@
package com.eactive.apim.portal.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
/**
* RestTemplate 설정 클래스
* 외부 API 호출을 위한 RestTemplate Bean을 제공합니다.
*
* @author sungpilhyun
* @since 2025-01-16
*/
@Configuration
public class RestTemplateConfig {
/**
* RestTemplate Bean 생성
*
* @return 설정된 RestTemplate 인스턴스
*/
@Bean
public RestTemplate restTemplate() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
// 연결 타임아웃: 10초
factory.setConnectTimeout(10000);
// 읽기 타임아웃: 30초
factory.setReadTimeout(30000);
return new RestTemplate(factory);
}
}