fix: EnsBatchRequireNew 신규 반영

- 배치 biz requiredNew service 분리
    -> @Transactional(propagation = Propagation.REQUIRES_NEW) 메소드 클래스 분리
dev
gitea-관리자 1 year ago
parent 64d760f107
commit 9eddbbdf78

@ -0,0 +1,261 @@
package kr.xit.biz.ens.service;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
import javax.validation.Validation;
import javax.validation.Validator;
import kr.xit.biz.common.ApiConstants;
import kr.xit.biz.common.ApiConstants.SndngSeCode;
import kr.xit.biz.ens.mapper.IEnsBatchMapper;
import kr.xit.biz.ens.model.EnsDTO;
import kr.xit.biz.ens.model.cntc.CntcDTO;
import kr.xit.biz.ens.model.kakao.KkopayDocBulkDTO.BulkSendResponses;
import kr.xit.biz.sms.service.ISmsMessageService;
import kr.xit.core.exception.BizRuntimeException;
import kr.xit.core.support.utils.Checks;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.egovframe.rte.fdl.cmmn.EgovAbstractServiceImpl;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
* <pre>
* description : class
* - {@code @Transactional(propagation = Propagation.REQUIRES_NEW)}
* - class Propagation.REQUIRES_NEW
* packageName : kr.xit.biz.ens.service
* fileName : EnsBatchRequireNewService
* author : limju
* date : 2023-08-31
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-08-31 limju
*
* </pre>
*/
@RequiredArgsConstructor
@Service
public class EnsBatchRequireNewService extends EgovAbstractServiceImpl implements
IEnsBatchRequireNewService {
private final IEnsBatchMapper mapper;
private final ISmsMessageService smsService;
private static final Validator validator = Validation.buildDefaultValidatorFactory()
.getValidator();
@Value("${file.cmm.upload.root}")
private String fileRoot;
@Value("${file.cmm.upload.post}")
private String filePost;
/**
*
*
* @param mstIdList List<String> ID
* @param resList List<KkopayDocBulkDTO.BulkSendResponses>
* @param unitySndMstId String ID
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveKkoMyDocResult(final List<String> mstIdList, String unitySndMstId,
final List<BulkSendResponses> resList) {
// 결과 반영
resList.forEach(o ->
o.getDocuments().forEach(
t -> {
// 카카오페이 문서요청 결과 반영
mapper.updateKakaoSendBulksResult(t);
// 모바일 페이지 컨텐트 생성
if (Checks.isNotEmpty(t.getDocument_binder_uuid())) {
//tgtDTO.setUnitySndngDetailId(t.getExternal_document_uuid());
mapper.insertMobilePageManage(t.getExternal_document_uuid());
}
// 연계발송결과 생성
insertKkoMyDocCntcSndngResult(SndngSeCode.KAKAO.getCode(),
t.getExternal_document_uuid(), t.getError_message());
})
);
// 마스터 상태 변경
updateKkoMyDocSendSndngMstStatus(mstIdList, unitySndMstId, "카카오 문서 발송요청 실패(발송마스터 데이타 오류)");
}
/**
* E-GREEN
*
* @param tgtDTO EnsDTO.SndngMssageParam
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void sendEgreen(final EnsDTO.SndngMssageParam tgtDTO) {
final List<EnsDTO.PostSndng> list = mapper.selectPostTgts(tgtDTO);
final String filePath = fileRoot + filePost;
final String fileName = list.get(0).getConKey() + ".txt";
if (!egreenFileWrite(filePath, fileName, list)) {
throw BizRuntimeException.create("우편 파일 생성 실패");
}
EnsDTO.SndngMssageParam paramDTO = EnsDTO.SndngMssageParam.builder()
.unitySndngMastrId(list.get(0).getUnitySndngMastrId())
.sndngMastrId(list.get(0).getSndngMastrId())
.newSndngProcessSttus(list.get(0).getSndngProcessSttus())
.build();
updateStepSendMststatus(paramDTO, "E-GREEN");
}
/**
* SMS - xit SMS : call
*
* @param tgtDTO EnsDTO.SndngMssageParam
* @see kr.xit.biz.sms.service.SmsMessageService#sendSmsList(List)
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void sendSms(final EnsDTO.SndngMssageParam tgtDTO) {
final List<EnsDTO.SmsSndng> list = mapper.selectSmsSendTgts(tgtDTO);
// Orcale DB - 서비스 분리
smsService.sendSmsList(list);
EnsDTO.SndngMssageParam paramDTO = EnsDTO.SndngMssageParam.builder()
.unitySndngMastrId(list.get(0).getUnitySndngMastrId())
.sndngMastrId(list.get(0).getSndngMastrId())
.newSndngProcessSttus(list.get(0).getSndngProcessSttus())
.build();
updateStepSendMststatus(paramDTO, "SMS");
}
//-----------------------------------------------------------------------------------------------------------------
/**
* (tb_cntc_sndng_result)
*
* @param sndngSeCode - KKO-MY-DOC|E-GREEN|SMS
* @param unitySndngDetailId ID
* @param errMessage (API )
*/
private void insertKkoMyDocCntcSndngResult(final String sndngSeCode,
final String unitySndngDetailId, final String errMessage) {
String rsltSts = StringUtils.defaultString(errMessage,
ApiConstants.DocBoxStatus.SENT.getCode());
mapper.insertCntcSndngResult(CntcDTO.SndngResult.builder()
.unitySndngDetailId(unitySndngDetailId)
.sndngSeCode(sndngSeCode)
.sndngResultSttus(
rsltSts.equals(ApiConstants.DocBoxStatus.SENT.getCode()) ? "SENT" : "FAIL")
.errorCn(errMessage)
.build());
}
/**
* / / send-ok|sending1|sending2
*
* @param mstIdList ID
* @param unitySndMstId String ID
* @param stsErrMsg String
*/
private void updateKkoMyDocSendSndngMstStatus(final List<String> mstIdList,
final String unitySndMstId, final String stsErrMsg) {
for (String id : mstIdList) {
EnsDTO.SndngMssageParam dto = mapper.selectSndProcessStatus(id)
.orElseThrow(() -> BizRuntimeException.create(stsErrMsg));
EnsDTO.SndngMssageParam paramDTO = EnsDTO.SndngMssageParam.builder()
.sndngMastrId(id)
.unitySndngMastrId(unitySndMstId)
.newSndngProcessSttus(dto.getNewSndngProcessSttus())
.build();
if (mapper.updateProcessSttusCntcSndngMst(paramDTO) != 1) {
throw BizRuntimeException.create("[send-카카오]연계 발송 마스터 상태변경 실패");
}
if (mapper.updateProcessSttusUnitySndngMst(paramDTO) != 1) {
throw BizRuntimeException.create("[send-카카오]통합 발송 마스터 상태변경 실패");
}
if (mapper.updateProcessSttusSndngMst(paramDTO) != 1) {
throw BizRuntimeException.create("[send-카카오]발송 마스터 상태변경 실패");
}
}
}
/**
* E-GREEN -
*
* @param filePath String
* @param fileName String
* @param list List<EnsDTO.PostSndng>
* @return boolean
*/
private boolean egreenFileWrite(final String filePath, final String fileName,
final List<EnsDTO.PostSndng> list) {
try {
File folder = new File(filePath);
if (!folder.exists()) //noinspection ResultOfMethodCallIgnored
{
folder.mkdirs();
}
File file = new File(filePath + fileName);
if (!file.exists()) {
if (!file.createNewFile()) {
return false;
}
}
try (BufferedWriter writer = new BufferedWriter(new FileWriter(file, true))) {
for (EnsDTO.PostSndng postSndng : list) {
writer.write(StringUtils.defaultString(postSndng.getConKey()) + "|");
writer.write(StringUtils.defaultString(postSndng.getSenderNm()) + "|");
writer.write(StringUtils.defaultString(postSndng.getSenderZipNo()) + "|");
writer.write(StringUtils.defaultString(postSndng.getSenderAddr()) + "|");
writer.write(StringUtils.defaultString(postSndng.getSenderDetailAddr()) + "|");
writer.write(StringUtils.defaultString(postSndng.getReceiverSendNo()) + "|");
writer.write(StringUtils.defaultString(postSndng.getReceiverNm()) + "|");
writer.write(StringUtils.defaultString(postSndng.getReceiverZipNo()) + "|");
writer.write(StringUtils.defaultString(postSndng.getReceiverAddr()) + "|");
writer.write(
StringUtils.defaultString(postSndng.getReceiverDetailAddr()) + "|");
writer.write(StringUtils.defaultString(postSndng.getSschnge1()) + "|");
writer.write(StringUtils.defaultString(postSndng.getSschnge2()) + "|");
writer.write(StringUtils.defaultString(postSndng.getSschnge3()));
writer.newLine();
}
writer.flush();
} catch (IOException e) {
throw BizRuntimeException.create(e.getMessage());
}
} catch (IOException ie) {
throw BizRuntimeException.create(ie.getMessage());
}
return true;
}
/**
*
*
* @param paramDTO EnsDTO.SndngMssageParam
* @param workCfg String
*/
private void updateStepSendMststatus(final EnsDTO.SndngMssageParam paramDTO,
final String workCfg) {
if (mapper.updateProcessSttusCntcSndngMst(paramDTO) != 1) {
throw BizRuntimeException.create(String.format("[send-%s]연계 발송 마스터 상태변경 실패", workCfg));
}
if (mapper.updateProcessSttusUnitySndngMst(paramDTO) != 1) {
throw BizRuntimeException.create(String.format("[send-%s]통합 발송 마스터 상태변경 실패", workCfg));
}
if (mapper.updateProcessSttusSndngMst(paramDTO) != 1) {
throw BizRuntimeException.create(String.format("[send-%s]발송 마스터 상태변경 실패", workCfg));
}
}
}

@ -2,10 +2,6 @@ package kr.xit.biz.ens.service;
import egovframework.com.cmm.util.EgovDateUtil;
import egovframework.com.cmm.util.EgovStringUtil;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@ -32,7 +28,6 @@ import kr.xit.biz.ens.model.kakao.KkopayDocBulkDTO.BulkSendResponses;
import kr.xit.biz.ens.model.kakao.KkopayDocBulkDTO.BulkStatusRequests;
import kr.xit.biz.ens.model.kakao.KkopayDocBulkDTO.BulkStatusResponses;
import kr.xit.biz.ens.model.kakao.KkopayDocBulkDTO.PropertyBulk;
import kr.xit.biz.sms.service.ISmsMessageService;
import kr.xit.core.exception.BizRuntimeException;
import kr.xit.core.model.ApiResponseDTO;
import kr.xit.core.spring.util.ApiWebClientUtil;
@ -42,14 +37,12 @@ import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.lang3.StringUtils;
import org.egovframe.rte.fdl.cmmn.EgovAbstractServiceImpl;
import org.egovframe.rte.fdl.cryptography.EgovPasswordEncoder;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
@ -79,10 +72,9 @@ public class EnsBatchService extends EgovAbstractServiceImpl implements IEnsBatc
private static final String profile = System.getProperty("spring.profiles.active");
private final IEnsBatchRequireNewService requireNewService;
private final ApiWebClientUtil apiWebClient;
private final IEnsBatchMapper mapper;
private final EgovPasswordEncoder encryptor;
private final ISmsMessageService smsService;
private static final Validator validator = Validation.buildDefaultValidatorFactory()
.getValidator();
@ -90,11 +82,6 @@ public class EnsBatchService extends EgovAbstractServiceImpl implements IEnsBatc
@Value("${contract.kakao.bulk-max-cnt}")
private int bulkMaxCnt;
@Value("${file.cmm.upload.root}")
private String fileRoot;
@Value("${file.cmm.upload.post}")
private String filePost;
private static final String SNDNG_PROCESS_STTUS = "sndngProcessSttus";
private static final String UNITY_SNDNG_MST_ID = "unitySndngMastrId";
private static final String YMDHMS = "yyyyMMddHHmmss";
@ -324,12 +311,12 @@ public class EnsBatchService extends EgovAbstractServiceImpl implements IEnsBatc
// E-GREEN
case E_GREEN:
sendEgreen(tgtDTO);
requireNewService.sendEgreen(tgtDTO);
break;
// SMS
case SMS:
sendSms(tgtDTO);
requireNewService.sendSms(tgtDTO);
break;
default:
@ -346,7 +333,7 @@ public class EnsBatchService extends EgovAbstractServiceImpl implements IEnsBatc
@Override
@Transactional
public void findKkoMyDocStatusBulks(final EnsDTO.BatchEnsRequest reqDTO) {
final String url = apiHost + apiBulkStatus;;
final String url = apiHost + apiBulkStatus;
final List<String> docsBinderUuids = mapper.selectKakaoStatusTgts(reqDTO.getSndngProcessSttus());
@ -438,6 +425,7 @@ public class EnsBatchService extends EgovAbstractServiceImpl implements IEnsBatc
* @param dto EnsDTO.SndngMssageParam
* @see TaskCmmUtils#taskEnsBatchServiceUpdateErrorLog
*/
@Transactional
public void updateErrorLog(EnsDTO.SndngMssageParam dto) {
if ("SndngAcceptJob".equals(dto.getErrorCode())) {
mapper.updateProcessSttusCntcSndngMst(dto);
@ -683,13 +671,8 @@ public class EnsBatchService extends EgovAbstractServiceImpl implements IEnsBatc
//-----------------------------------------------------------------------------------------------------------------
// send
//-----------------------------------------------------------------------------------------------------------------
/**
*
*
* @param tgtDTO EnsDTO.SndngMssageParam
*/
@Transactional
public void sendBulkKakaoMyDocs(final EnsDTO.SndngMssageParam tgtDTO) {
private void sendBulkKakaoMyDocs(final EnsDTO.SndngMssageParam tgtDTO) {
final String url = apiHost + apiBulkSend;
final List<SendKakaoTgt> list = mapper.selectKakaoSendTgts(tgtDTO);
@ -731,39 +714,7 @@ public class EnsBatchService extends EgovAbstractServiceImpl implements IEnsBatc
.collect(Collectors.toList());
// 카카오 문서 요청 결과 반영
saveKkoMyDocResult(mstIdList, tgtDTO.getUnitySndngMastrId(), resList);
}
/**
*
*
* @param mstIdList List<String> ID
* @param resList List<KkopayDocBulkDTO.BulkSendResponses>
* @param unitySndMstId String ID
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveKkoMyDocResult(final List<String> mstIdList, String unitySndMstId,
final List<BulkSendResponses> resList) {
// 결과 반영
resList.forEach(o ->
o.getDocuments().forEach(
t -> {
// 카카오페이 문서요청 결과 반영
mapper.updateKakaoSendBulksResult(t);
// 모바일 페이지 컨텐트 생성
if (Checks.isNotEmpty(t.getDocument_binder_uuid())) {
//tgtDTO.setUnitySndngDetailId(t.getExternal_document_uuid());
mapper.insertMobilePageManage(t.getExternal_document_uuid());
}
// 연계발송결과 생성
insertKkoMyDocCntcSndngResult(ApiConstants.SndngSeCode.KAKAO.getCode(),
t.getExternal_document_uuid(), t.getError_message());
})
);
// 마스터 상태 변경
updateKkoMyDocSendSndngMstStatus(mstIdList, unitySndMstId, "카카오 문서 발송요청 실패(발송마스터 데이타 오류)");
requireNewService.saveKkoMyDocResult(mstIdList, tgtDTO.getUnitySndngMastrId(), resList);
}
/**
@ -884,183 +835,13 @@ public class EnsBatchService extends EgovAbstractServiceImpl implements IEnsBatc
}
}
/**
* E-GREEN
*
* @param tgtDTO EnsDTO.SndngMssageParam
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void sendEgreen(final EnsDTO.SndngMssageParam tgtDTO) {
final List<EnsDTO.PostSndng> list = mapper.selectPostTgts(tgtDTO);
final String filePath = fileRoot + filePost;
final String fileName = list.get(0).getConKey() + ".txt";
if (!egreenFileWrite(filePath, fileName, list)) {
throw BizRuntimeException.create("우편 파일 생성 실패");
}
EnsDTO.SndngMssageParam paramDTO = EnsDTO.SndngMssageParam.builder()
.unitySndngMastrId(list.get(0).getUnitySndngMastrId())
.sndngMastrId(list.get(0).getSndngMastrId())
.newSndngProcessSttus(list.get(0).getSndngProcessSttus())
.build();
updateStepSendMststatus(paramDTO, "E-GREEN");
}
/**
* E-GREEN -
*
* @param filePath String
* @param fileName String
* @param list List<EnsDTO.PostSndng>
* @return boolean
*/
private boolean egreenFileWrite(final String filePath, final String fileName,
final List<EnsDTO.PostSndng> list) {
try {
File folder = new File(filePath);
if (!folder.exists()) //noinspection ResultOfMethodCallIgnored
{
folder.mkdirs();
}
File file = new File(filePath + fileName);
if (!file.exists()) {
if (!file.createNewFile()) {
return false;
}
;
}
try (BufferedWriter writer = new BufferedWriter(new FileWriter(file, true))) {
for (EnsDTO.PostSndng postSndng : list) {
writer.write(StringUtils.defaultString(postSndng.getConKey()) + "|");
writer.write(StringUtils.defaultString(postSndng.getSenderNm()) + "|");
writer.write(StringUtils.defaultString(postSndng.getSenderZipNo()) + "|");
writer.write(StringUtils.defaultString(postSndng.getSenderAddr()) + "|");
writer.write(StringUtils.defaultString(postSndng.getSenderDetailAddr()) + "|");
writer.write(StringUtils.defaultString(postSndng.getReceiverSendNo()) + "|");
writer.write(StringUtils.defaultString(postSndng.getReceiverNm()) + "|");
writer.write(StringUtils.defaultString(postSndng.getReceiverZipNo()) + "|");
writer.write(StringUtils.defaultString(postSndng.getReceiverAddr()) + "|");
writer.write(
StringUtils.defaultString(postSndng.getReceiverDetailAddr()) + "|");
writer.write(StringUtils.defaultString(postSndng.getSschnge1()) + "|");
writer.write(StringUtils.defaultString(postSndng.getSschnge2()) + "|");
writer.write(StringUtils.defaultString(postSndng.getSschnge3()));
writer.newLine();
}
writer.flush();
}
} catch (IOException ie) {
return false;
}
return true;
}
/**
* SMS - xit SMS : call
*
* @param tgtDTO EnsDTO.SndngMssageParam
* @see kr.xit.biz.sms.service.SmsMessageService#sendSmsList(List)
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void sendSms(final EnsDTO.SndngMssageParam tgtDTO) {
final List<EnsDTO.SmsSndng> list = mapper.selectSmsSendTgts(tgtDTO);
// Orcale DB - 서비스 분리
smsService.sendSmsList(list);
EnsDTO.SndngMssageParam paramDTO = EnsDTO.SndngMssageParam.builder()
.unitySndngMastrId(list.get(0).getUnitySndngMastrId())
.sndngMastrId(list.get(0).getSndngMastrId())
.newSndngProcessSttus(list.get(0).getSndngProcessSttus())
.build();
updateStepSendMststatus(paramDTO, "SMS");
}
/**
*
*
* @param paramDTO EnsDTO.SndngMssageParam
* @param workCfg String
*/
@Transactional
public void updateStepSendMststatus(final EnsDTO.SndngMssageParam paramDTO,
final String workCfg) {
if (mapper.updateProcessSttusCntcSndngMst(paramDTO) != 1) {
throw BizRuntimeException.create(String.format("[send-%s]연계 발송 마스터 상태변경 실패", workCfg));
}
if (mapper.updateProcessSttusUnitySndngMst(paramDTO) != 1) {
throw BizRuntimeException.create(String.format("[send-%s]통합 발송 마스터 상태변경 실패", workCfg));
}
if (mapper.updateProcessSttusSndngMst(paramDTO) != 1) {
throw BizRuntimeException.create(String.format("[send-%s]발송 마스터 상태변경 실패", workCfg));
}
}
/**
* (tb_cntc_sndng_result)
*
* @param sndngSeCode - KKO-MY-DOC|E-GREEN|SMS
* @param unitySndngDetailId ID
* @param errMessage (API )
*/
@Transactional
public void insertKkoMyDocCntcSndngResult(final String sndngSeCode,
final String unitySndngDetailId, final String errMessage) {
String rsltSts = StringUtils.defaultString(errMessage,
ApiConstants.DocBoxStatus.SENT.getCode());
mapper.insertCntcSndngResult(CntcDTO.SndngResult.builder()
.unitySndngDetailId(unitySndngDetailId)
.sndngSeCode(sndngSeCode)
.sndngResultSttus(
rsltSts.equals(ApiConstants.DocBoxStatus.SENT.getCode()) ? "SENT" : "FAIL")
.errorCn(errMessage)
.build());
}
/**
* / / send-ok|sending1|sending2
*
* @param mstIdList ID
* @param unitySndMstId String ID
* @param stsErrMsg
*/
@Transactional
public void updateKkoMyDocSendSndngMstStatus(final List<String> mstIdList,
final String unitySndMstId, final String stsErrMsg) {
for (String id : mstIdList) {
EnsDTO.SndngMssageParam dto = mapper.selectSndProcessStatus(id)
.orElseThrow(() -> BizRuntimeException.create(stsErrMsg));
EnsDTO.SndngMssageParam paramDTO = EnsDTO.SndngMssageParam.builder()
.sndngMastrId(id)
.unitySndngMastrId(unitySndMstId)
.newSndngProcessSttus(dto.getNewSndngProcessSttus())
.build();
if (mapper.updateProcessSttusCntcSndngMst(paramDTO) != 1) {
throw BizRuntimeException.create("[send-카카오]연계 발송 마스터 상태변경 실패");
}
if (mapper.updateProcessSttusUnitySndngMst(paramDTO) != 1) {
throw BizRuntimeException.create("[send-카카오]통합 발송 마스터 상태변경 실패");
}
if (mapper.updateProcessSttusSndngMst(paramDTO) != 1) {
throw BizRuntimeException.create("[send-카카오]발송 마스터 상태변경 실패");
}
}
}
/**
* -
*
* @param mstIdList List<String>
* @param stsErrMsg String
*/
@Transactional
public void updateKkoMyDocSndngMstFailStatus(final List<String> mstIdList,
private void updateKkoMyDocSndngMstFailStatus(final List<String> mstIdList,
final String stsErrMsg) {
for (String id : mstIdList) {

@ -0,0 +1,29 @@
package kr.xit.biz.ens.service;
import java.util.List;
import kr.xit.biz.ens.model.EnsDTO;
import kr.xit.biz.ens.model.kakao.KkopayDocBulkDTO.BulkSendResponses;
/**
* <pre>
* description : class
* - {@code @Transactional(propagation = Propagation.REQUIRES_NEW)}
* - class Propagation.REQUIRES_NEW
*
* packageName : kr.xit.biz.ens.service
* fileName : IEnsBatchRequireNewService
* author : limju
* date : 2023-08-31
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-08-31 limju
*
* </pre>
*/
public interface IEnsBatchRequireNewService {
void saveKkoMyDocResult(final List<String> mstIdList, String unitySndMstId, final List<BulkSendResponses> resList);
void sendEgreen(final EnsDTO.SndngMssageParam tgtDTO);
void sendSms(final EnsDTO.SndngMssageParam tgtDTO);
}
Loading…
Cancel
Save