From 91b28e38cf8296ef33d825c844451009f95bbad6 Mon Sep 17 00:00:00 2001 From: Kurt92 Date: Wed, 24 Sep 2025 16:19:20 +0900 Subject: [PATCH] =?UTF-8?q?feat=20:=20epost=201=EC=B0=A8=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C,=20=EA=B0=9C=EB=B0=9C=ED=95=98=EB=A9=B4=EC=84=9C=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=EB=A5=BC=20=EB=AA=BB=EB=8F=8C?= =?UTF-8?q?=EB=A0=B8=EC=9D=8C.=20=EC=82=AC=EC=9D=B4=EB=93=9C=20=EC=9D=B4?= =?UTF-8?q?=ED=8C=A9=ED=8A=B8=20=EB=A7=8E=EC=9D=84=EA=B2=83=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=98=88=EC=83=81=EB=90=A8.=20todo=20:=20=ED=86=B5?= =?UTF-8?q?=ED=95=A9=20=EC=9A=B0=ED=8E=B8=EC=84=9C=EB=B2=84=20sftp?= =?UTF-8?q?=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 +- .../worker/domain/entity/EpostRgstNmbr.java | 31 ++ .../repo/cp/CpEPostMakeResultRepository.java | 4 +- .../domain/repo/cp/CpEPostRgstNmbr.java | 10 + .../repo/ep/EpEPostMakeResultRepository.java | 3 + .../domain/repo/ep/EpEPostRgstNmbr.java | 9 + .../worker/scheduler/epost/dto/EPostDto.java | 45 +-- .../repository/EPostQueryDslRepository.java | 82 ++++- .../epost/schedule/EPostScheduler.java | 40 ++- .../scheduler/epost/service/EPostService.java | 285 ++++++++++++++---- .../com/worker/util/common/CommonUtils.java | 28 +- .../worker/util/zipFileMaker/ZipMaker.java | 95 ++++++ 12 files changed, 499 insertions(+), 141 deletions(-) create mode 100644 src/main/java/com/worker/domain/entity/EpostRgstNmbr.java create mode 100644 src/main/java/com/worker/domain/repo/cp/CpEPostRgstNmbr.java create mode 100644 src/main/java/com/worker/domain/repo/ep/EpEPostRgstNmbr.java diff --git a/README.md b/README.md index ad16361..347706f 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ start "" "C:\Program Files\Eclipse Adoptium\jdk-17.0.15.6-hotspot\bin\java.exe" - 대신 로그를 볼려면 실행로그파일을 따로 지정해줘야 된다.
-![스크린샷 2025-09-09 오전 10.13.50.png](../../../../../var/folders/qj/hwm278q51bg1yghyw_bybl5h0000gn/T/TemporaryItems/NSIRD_screencaptureui_TNPAv2/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202025-09-09%20%EC%98%A4%EC%A0%84%2010.13.50.png) + - 수동실행을 위한 뷰페이지 localhost:8011로 접속하면됨. - 로그보기를 누르면 실시간 로그도 볼수 있음. - 프로그램 실행유무는 로컬호스트 8011을 접속 가능 여부로 판단하면됨. @@ -82,7 +82,7 @@ exit /b 0 - setinfo는 멀티 PK임. CODE_NAME, GROUP_CODE, DETAIL_CODE [신문고] -- CODE_NANE(PK) : WORKER +- CODE_NAME(PK) : WORKER - GROUP_CODE(PK) : INFO - DETAIL_CODE(PK) : PROD - INT_VALUE1 : 메인 시군구 코드 @@ -95,7 +95,7 @@ exit /b 0 - STR_VALUE6 : 하위 모든 시군구 + 부서코드 제이슨 형태 ex) {"수정구":[41131, 3790009], "중원구":[41133, 3800009], "분당구": [41135, 3810070]} [E-Post] -- CODE_NANE(PK) : +- CODE_NAME(PK) : - GROUP_CODE(PK) : - DETAIL_CODE(PK) : - INT_VALUE1 : @@ -108,7 +108,7 @@ exit /b 0 - STR_VALUE6 : -- CODE_NANE(PK) : +- CODE_NAME(PK) : - GROUP_CODE(PK) : - DETAIL_CODE(PK) : - INT_VALUE1 : diff --git a/src/main/java/com/worker/domain/entity/EpostRgstNmbr.java b/src/main/java/com/worker/domain/entity/EpostRgstNmbr.java new file mode 100644 index 0000000..5f74a94 --- /dev/null +++ b/src/main/java/com/worker/domain/entity/EpostRgstNmbr.java @@ -0,0 +1,31 @@ +package com.worker.domain.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Table( + name = "epost_rgst_nmbr", + indexes = { + @Index(name = "EPOST_RGST_NMBR_IDX1", columnList = "PCURSOR") + } +) +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class EpostRgstNmbr { + + @Id + @Column(name = "RGST_NMBR", columnDefinition = "char(13)", nullable = false) + private String rgstNmbr; + + @Column(name = "RGST_NMBR_NEXT", columnDefinition = "char(13)") + private String rgstNmbrNext; + + @Column(name = "PCURSOR", columnDefinition = "char(1)") + private String pcursor; +} \ No newline at end of file diff --git a/src/main/java/com/worker/domain/repo/cp/CpEPostMakeResultRepository.java b/src/main/java/com/worker/domain/repo/cp/CpEPostMakeResultRepository.java index 9fd2620..4f77d24 100644 --- a/src/main/java/com/worker/domain/repo/cp/CpEPostMakeResultRepository.java +++ b/src/main/java/com/worker/domain/repo/cp/CpEPostMakeResultRepository.java @@ -3,6 +3,8 @@ package com.worker.domain.repo.cp; import com.worker.domain.entity.EpostMakeResult; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; + public interface CpEPostMakeResultRepository extends JpaRepository { - EpostMakeResult findByConkeyAndRgstNmbr(); + Optional findByConKeyAndRgstNmbr(String conKey, String rgstNmbr); } diff --git a/src/main/java/com/worker/domain/repo/cp/CpEPostRgstNmbr.java b/src/main/java/com/worker/domain/repo/cp/CpEPostRgstNmbr.java new file mode 100644 index 0000000..603be4a --- /dev/null +++ b/src/main/java/com/worker/domain/repo/cp/CpEPostRgstNmbr.java @@ -0,0 +1,10 @@ +package com.worker.domain.repo.cp; + +import com.worker.domain.entity.EpostRgstNmbr; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface CpEPostRgstNmbr extends JpaRepository { + EpostRgstNmbr findByPcursor(String cursor); +} diff --git a/src/main/java/com/worker/domain/repo/ep/EpEPostMakeResultRepository.java b/src/main/java/com/worker/domain/repo/ep/EpEPostMakeResultRepository.java index 53dbe53..626aedc 100644 --- a/src/main/java/com/worker/domain/repo/ep/EpEPostMakeResultRepository.java +++ b/src/main/java/com/worker/domain/repo/ep/EpEPostMakeResultRepository.java @@ -3,5 +3,8 @@ package com.worker.domain.repo.ep; import com.worker.domain.entity.EpostMakeResult; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; + public interface EpEPostMakeResultRepository extends JpaRepository { + Optional findByConKeyAndRgstNmbr(String conKey, String rgstNmbr); } diff --git a/src/main/java/com/worker/domain/repo/ep/EpEPostRgstNmbr.java b/src/main/java/com/worker/domain/repo/ep/EpEPostRgstNmbr.java new file mode 100644 index 0000000..7526404 --- /dev/null +++ b/src/main/java/com/worker/domain/repo/ep/EpEPostRgstNmbr.java @@ -0,0 +1,9 @@ +package com.worker.domain.repo.ep; + +import com.worker.domain.entity.EpostRgstNmbr; +import com.worker.domain.repo.cp.CpEPostRgstNmbr; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface EpEPostRgstNmbr extends JpaRepository { + EpostRgstNmbr findByPcursor(String number); +} diff --git a/src/main/java/com/worker/scheduler/epost/dto/EPostDto.java b/src/main/java/com/worker/scheduler/epost/dto/EPostDto.java index 36e3af1..a7de33a 100644 --- a/src/main/java/com/worker/scheduler/epost/dto/EPostDto.java +++ b/src/main/java/com/worker/scheduler/epost/dto/EPostDto.java @@ -5,6 +5,7 @@ import jakarta.persistence.Column; import lombok.*; import java.util.List; +import java.util.Optional; public class EPostDto { @@ -61,8 +62,14 @@ public class EPostDto { @Builder public static class Key { private String conKey; + private String tgGb; private String tgCode; + private String postProcStt; + private String regYmd; + private String tgUnitySndngMastrId; + private String tgPostYn; } + @Getter @Setter @NoArgsConstructor @@ -70,37 +77,17 @@ public class EPostDto { @Builder public static class Target { - //cp_gojit - + //epost_sender_result private Long tgCode; - private String tgSggCode; - private String tgLawGb; - private String tgGb; - private String tgDlgb; - private String tgSrcSdate; - private String tgSrcEdate; - private String tgSdate; - private String tgEdate; - private String tgTitle; - private String tgDocno; - private String tgEtc; - private Integer tgTotcount; - private Long tgTotkeum; - private String tgIndt; - private Integer tgInuser; - private String tgConKey; - private String tgPostSeCd; - private String tgIsResend; - private String tgNoticeKey; - private String tgPostProcStt; - private String tgUnitySndngMastrId; - private String tgDelete; - private String tgPostYn; - private String tgElpostYn; + private String ConKey; - //epost_sender_reg - private String conKey; - private String regYmd; + + //epost_sender_detail + private String rgstNmbr; + private String recevSeq; //mm_code + + //tb_cntc_sndng_result + private String sndngResultSttus; } } diff --git a/src/main/java/com/worker/scheduler/epost/repository/EPostQueryDslRepository.java b/src/main/java/com/worker/scheduler/epost/repository/EPostQueryDslRepository.java index 50712ab..9d3fcb3 100644 --- a/src/main/java/com/worker/scheduler/epost/repository/EPostQueryDslRepository.java +++ b/src/main/java/com/worker/scheduler/epost/repository/EPostQueryDslRepository.java @@ -1,8 +1,10 @@ package com.worker.scheduler.epost.repository; import com.querydsl.core.types.Projections; +import com.querydsl.core.types.dsl.Expressions; import com.querydsl.jpa.impl.JPAQueryFactory; import com.worker.domain.entity.CpGojiSendHist; +import com.worker.domain.entity.EpostRgstNmbr; import com.worker.scheduler.epost.dto.EPostDto; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -18,6 +20,7 @@ import static com.worker.domain.entity.QCpGojit.cpGojit; import static com.worker.domain.entity.QCpInstruct.cpInstruct; import static com.worker.domain.entity.QCpInstructAnswer.cpInstructAnswer; import static com.worker.domain.entity.QEpostDelivResult.epostDelivResult; +import static com.worker.domain.entity.QEpostRgstNmbr.epostRgstNmbr; import static com.worker.domain.entity.QEpostSenderDetail.epostSenderDetail; import static com.worker.domain.entity.QEpostSenderReg.epostSenderReg; import static com.worker.domain.entity.QTbCntcSndngDetail.tbCntcSndngDetail; @@ -36,7 +39,12 @@ public class EPostQueryDslRepository { Projections.fields( EPostDto.SendTarget.Key.class, cpGojit.tgCode, - epostSenderReg.conKey + cpGojit.tgGb, + epostSenderReg.conKey, + epostSenderReg.postProcStt, + epostSenderReg.regYmd, + Expressions.as(Expressions.nullExpression(String.class), "tgPostYn"), + Expressions.as(Expressions.nullExpression(String.class), "tgUnitySndngMastrId") ) ) .from(epostSenderReg) @@ -49,6 +57,7 @@ public class EPostQueryDslRepository { } + // 일반 발송 public List findSendTargets(JPAQueryFactory queryFactory, List keys) { List tgCode = keys.stream().map(EPostDto.SendTarget.Key::getTgCode).collect(Collectors.toList()); @@ -58,7 +67,10 @@ public class EPostQueryDslRepository { .select( Projections.fields( EPostDto.SendTarget.Target.class, - cpGojit.tgCode + epostSenderReg.tgCode, + epostSenderReg.conKey, + epostSenderDetail.rgstNmbr, + epostSenderDetail.recevSeq ) ) .from(epostSenderReg) @@ -78,16 +90,23 @@ public class EPostQueryDslRepository { return result; } + // 전자고지를 쓰는 서버 발송 + // 전자고지 테이블 조인함. public List findSendEgojiTargets(JPAQueryFactory queryFactory, List keys) { List tgCode = keys.stream().map(EPostDto.SendTarget.Key::getTgCode).collect(Collectors.toList()); List conKey = keys.stream().map(EPostDto.SendTarget.Key::getConKey).collect(Collectors.toList()); + List unitySndngMastrId = keys.stream().map(EPostDto.SendTarget.Key::getTgUnitySndngMastrId).collect(Collectors.toList()); List result = queryFactory .select( Projections.fields( EPostDto.SendTarget.Target.class, - cpGojit.tgCode + epostSenderReg.tgCode, + epostSenderReg.conKey, + epostSenderDetail.rgstNmbr, + epostSenderDetail.recevSeq, + tbCntcSndngResult.sndngResultSttus ) ) .from(epostSenderReg) @@ -97,15 +116,13 @@ public class EPostQueryDslRepository { cpInstruct.itSggcode.eq(cpInstructAnswer.id.iaSggcode), cpInstruct.itCause.eq(cpInstructAnswer.id.iaCode) ) - .leftJoin(tbCntcSndngDetail).on( - epostSenderDetail.recevSeq.eq(tbCntcSndngDetail.mainCode) - ) + .leftJoin(tbCntcSndngDetail).on(epostSenderDetail.recevSeq.eq(tbCntcSndngDetail.mainCode)) .leftJoin(tbCntcSndngResult).on(tbCntcSndngDetail.unitySndngDetailId.eq(tbCntcSndngResult.unitySndngDetailId)) .where( epostSenderReg.postProcStt.eq("01"), epostSenderReg.tgCode.in(tgCode), epostSenderReg.conKey.in(conKey), - tbCntcSndngDetail.unitySndngMastrId.eq(""), + tbCntcSndngDetail.unitySndngMastrId.in(unitySndngMastrId), tbCntcSndngResult.sndngResultSttus.ne("READ") .and(tbCntcSndngResult.sndngResultSttus.isNull() ) @@ -237,7 +254,7 @@ public class EPostQueryDslRepository { .fetch(); -// updateRceptResult(queryFactory, dtos, results); + updateRecvResult(queryFactory, dtos, results); return results; } @@ -314,6 +331,30 @@ public class EPostQueryDslRepository { } + public void updateSenderRegPostStt(JPAQueryFactory queryFactory, List cpSendTargets) { + + List conKeys = cpSendTargets.stream() + .map(post -> post.getConKey()) + .collect(Collectors.toUnmodifiableList()); + + queryFactory.update(epostSenderReg) + .set(epostSenderReg.postProcStt, "03") + .where(epostSenderReg.conKey.in(conKeys)) + .execute(); + } + + public void updateGojitPostYn(JPAQueryFactory queryFactory, List cpSendEgojiTargets) { + + List conKeys = cpSendEgojiTargets.stream() + .map(post -> post.getConKey()) + .collect(Collectors.toUnmodifiableList()); + + queryFactory.update(cpGojit) + .set(cpGojit.tgPostYn, "Y") + .where(cpGojit.tgConKey.in(conKeys)) + .execute(); + } + public Long findEpostDelivResultMaxKey(JPAQueryFactory queryFactory, String conKey) { return queryFactory .select(epostDelivResult.seqKey.max()) @@ -330,4 +371,29 @@ public class EPostQueryDslRepository { .fetchOne(); } + + public void updateOldPostToCancel(JPAQueryFactory queryFactory, List oldPosts) { + + List conKeys = oldPosts.stream() + .map(post -> post.getConKey()) + .collect(Collectors.toUnmodifiableList()); + + queryFactory.update(epostSenderReg) + .set(epostSenderReg.postProcStt, "02") + .where(epostSenderReg.conKey.in(conKeys)) + .execute(); + } + + public void updateTrgstNmbr(JPAQueryFactory queryFactory, EpostRgstNmbr trgstNmbr){ + queryFactory.update(epostRgstNmbr) + .set(epostRgstNmbr.pcursor, "0") + .where(epostRgstNmbr.rgstNmbr.eq(trgstNmbr.getRgstNmbr())) + .execute(); + + queryFactory.update(epostRgstNmbr) + .set(epostRgstNmbr.pcursor, "1") + .where(epostRgstNmbr.rgstNmbr.eq(trgstNmbr.getRgstNmbrNext())) + .execute(); + } + } diff --git a/src/main/java/com/worker/scheduler/epost/schedule/EPostScheduler.java b/src/main/java/com/worker/scheduler/epost/schedule/EPostScheduler.java index 69ce5c7..c3c8c28 100644 --- a/src/main/java/com/worker/scheduler/epost/schedule/EPostScheduler.java +++ b/src/main/java/com/worker/scheduler/epost/schedule/EPostScheduler.java @@ -28,15 +28,13 @@ public class EPostScheduler { private final FileReader fileReader; + /** + * 파일 연계 Send + * 발송대상 조회 후 발송 text파일 생성 + * */ // @Scheduled(fixedRate = 10 * 60 * 1000L) // 10분 public void ePostSendScheduler() { - /** - * 파일 연계 - * 종적결과 폴링, 디비조회 후 text 생성 - * */ - - // esb경로 info EPostDto.SetInfo setInfo = epostSetinfoService.findSetInfo(); @@ -44,24 +42,13 @@ public class EPostScheduler { ePostService.findEPostSendTarget(setInfo); - - - - - - - - //발송대상 찾기 - //findEPostSendTarget() - - //대상 text만들기 - - - - - } + + /** + * 파일 연계 Recv + * 종적결과 폴링 + * */ // @Scheduled(fixedRate = 10 * 60 * 1000) // 10분 public void ePostRcvScheduler() { @@ -86,5 +73,14 @@ public class EPostScheduler { } + /** + * 통합 우편서버를 사용하는경우 결과파일이 통합서버로 떨어짐 + * 해당 서버 sftp로 파일 가져와야함. + * */ + // @Scheduled(fixedRate = 10 * 60 * 1000) // 10분 + public void getFileFromIntergrationServer() { + + } + } diff --git a/src/main/java/com/worker/scheduler/epost/service/EPostService.java b/src/main/java/com/worker/scheduler/epost/service/EPostService.java index 3bf3eca..96b3f01 100644 --- a/src/main/java/com/worker/scheduler/epost/service/EPostService.java +++ b/src/main/java/com/worker/scheduler/epost/service/EPostService.java @@ -8,6 +8,8 @@ import com.worker.domain.repo.cp.*; import com.worker.domain.repo.ep.*; import com.worker.scheduler.epost.dto.EPostDto; import com.worker.scheduler.epost.repository.EPostQueryDslRepository; +import com.worker.util.common.CommonUtils; +import com.worker.util.zipFileMaker.ZipMaker; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -26,6 +28,8 @@ import java.util.stream.Collectors; public class EPostService { private final Environment env; + private final CommonUtils utils; + private final ZipMaker zipMaker; private final ObjectMapper objectMapper; private final CpSetinfoRepository cpSetinfoRepository; @@ -44,6 +48,8 @@ public class EPostService { private final EpEPostSenderDetailRepository epEPostSenderDetailRepository; private final CpEPostSenderRegRepository cpEPostSenderRegRepository; private final EpEPostSenderRegRepository epEPostSenderRegRepository; + private final CpEPostRgstNmbr cpEPostRgstNmbr; + private final EpEPostRgstNmbr epEPostRgstNmbr; private final EPostQueryDslRepository ePostQueryDslRepository; @@ -66,56 +72,120 @@ public class EPostService { List cpSendTargetKeys = new ArrayList<>(); List epSendTargetKeys = new ArrayList<>(); + List cpSendEpostTargetKeys = new ArrayList<>(); + List epSendEpostTargetKeys = new ArrayList<>(); + List cpSendEgojiTargetKeys = new ArrayList<>(); + List epSendEgojiTargetKeys = new ArrayList<>(); List cpSendTargets = new ArrayList<>(); List epSendTargets = new ArrayList<>(); + List cpSendEgojiTargets = new ArrayList<>(); + List epSendEgojiTargets = new ArrayList<>(); // 타겟의 키,code 조회 - //cGojit + // getTgPostYn 이 전자고지를 안쓰는 경우 컬럼자체가 존재하지 않음. + // 해서 queryDsl에서는 익스프래셔으로 널처리 + //Gojit if(setInfo.getCpEPostInfo() != null) cpSendTargetKeys = ePostQueryDslRepository.findSendTargetKeys(cpQueryFactory); if(setInfo.getEpEPostInfo() != null) epSendTargetKeys = ePostQueryDslRepository.findSendTargetKeys(epQueryFactory); + // 전자고지/일반 대상 분리 + // getTgPostYn 널처리 + // 스트림에서는 오브젝트 이퀄로 널처리 (null 이면 걸러짐) + cpSendEgojiTargetKeys = cpSendTargetKeys.stream() + .filter(target -> Objects.equals(target.getTgPostYn(), "E")) + .toList(); + epSendEgojiTargetKeys = epSendTargetKeys.stream() + .filter(target -> Objects.equals(target.getTgPostYn(), "E")) + .toList(); + cpSendEpostTargetKeys = cpSendTargetKeys.stream() + .filter(target -> target.getTgPostYn() == null || target.getTgPostYn().equals("N")) + .toList(); + epSendEpostTargetKeys = epSendTargetKeys.stream() + .filter(target -> target.getTgPostYn() == null || target.getTgPostYn().equals("N")) + .toList(); + + + + + // 조회한 키로 cpSendTargets 의 상세내용 조회 + // qryFor + // 일반 + if(!cpSendEpostTargetKeys.isEmpty()) cpSendTargets = ePostQueryDslRepository.findSendTargets(cpQueryFactory, cpSendTargetKeys); + if(!epSendEpostTargetKeys.isEmpty()) epSendTargets = ePostQueryDslRepository.findSendTargets(epQueryFactory, epSendTargetKeys); + if(setInfo.getCpIsEns() || setInfo.getEpIsEns()) { // 전자고지 대상 조회 // qryEgoji - if(setInfo.getCpIsEns()) cpSendTargets = ePostQueryDslRepository.findSendEgojiTargets(cpQueryFactory, cpSendTargetKeys); - if(setInfo.getEpIsEns()) epSendTargets = ePostQueryDslRepository.findSendEgojiTargets(epQueryFactory, epSendTargetKeys); - - } else { - // 조회한 키로 cpSendTargets 의 상세내용 조회 - // qryFor - cpSendTargets = ePostQueryDslRepository.findSendTargets(cpQueryFactory, cpSendTargetKeys); - epSendTargets = ePostQueryDslRepository.findSendTargets(epQueryFactory, epSendTargetKeys); + if (setInfo.getCpIsEns()) + cpSendEgojiTargets = ePostQueryDslRepository.findSendEgojiTargets(cpQueryFactory, cpSendEgojiTargetKeys); + if (setInfo.getEpIsEns()) + epSendEgojiTargets = ePostQueryDslRepository.findSendEgojiTargets(epQueryFactory, epSendEgojiTargetKeys); } - - // REG_YMD 오늘기준 30일 이전 자료들 취소(POST_PROC_STT = '02') 로 변경 + try { + cancelOldPost(cpSendTargetKeys, epSendTargetKeys); + } catch (Exception e) { + log.error("30일 이전 자료들 취소 실패: cpKeys={}, epKeys={}", cpSendTargetKeys == null ? 0 : cpSendTargetKeys.size(), epSendTargetKeys == null ? 0 : epSendTargetKeys.size(), e); + throw e; + } //notice(안내문구 인거같음) - //goit notifce 조인 - - + //goit notice 조인 + // notice 안씀. 관련 로직 폐기해도 됨. + + + // 사전통보 or 계도 대상에서 mmcode만 추출 (prt_gubun = '0'사전통보 || prt_gubun = 'A' 계도) + // 사전 & 계도는 사진 같이 나감. + final List cpTargets = Optional.ofNullable(cpSendTargets).orElseGet(List::of); + List targetCpMmCodes = cpSendTargetKeys.stream() + .filter(o -> "0".equals(o.getTgGb()) || "A".equals(o.getTgGb())) + .flatMap(o -> cpTargets.stream() + .filter(t -> Objects.equals(o.getConKey(), t.getConKey())) + .map(EPostDto.SendTarget.Target::getRecevSeq)) + .filter(Objects::nonNull) + .distinct() + .toList(); + + final List epTargets = Optional.ofNullable(epSendTargets).orElseGet(List::of); + List targetEpMmCodes = epSendTargetKeys.stream() + .filter(o -> "0".equals(o.getTgGb()) || "A".equals(o.getTgGb())) + .flatMap(o -> epTargets.stream() + .filter(t -> Objects.equals(o.getConKey(), t.getConKey())) + .map(EPostDto.SendTarget.Target::getRecevSeq)) + .filter(Objects::nonNull) + .distinct() + .toList(); + + // mmcode로 이미지 파일 찾아서 zip생성 + try { + zipMaker.generateZipFile(targetCpMmCodes, targetEpMmCodes, setInfo); + } catch (Exception e) { + log.error("ZIP 파일 생성 실패 : " + e.getMessage()); + } - // SendTargets 돌면서 전자고지인지 체크후 후처리 - // 이거 필요 없을듯. 전자고지인지 체크후 그냥 빼버리면됨. - // 아래 스트림에서 전자고지 아닌것만 으로 스트림 돌리면됨. + // SendTargets 돌면서 전자고지인지 체크 + // 거기서 대기상태("E") 인 얘들의 등기번호를 업데이트 + // cpSendEgojiTargetKeys 등기번호 업데이트 하면 될듯 + egojiNonReadTargetsUpdateTrgstNmbr(cpSendEgojiTargets, epSendEgojiTargets); - // SendTargets 돌면서 전자고지 아닌거 후처리 하는데 - // 이거 그냥 리스트를 나눠서 따로따로 진행하는게 더 깔끔함 - // steam 으로 전송대상(전자고지 아닌것) 만 추출하셈 - // 전송대상은 tg_post_yn 이 'E' 이거나 'Y' 인것만임. - // E는 대기상태로 전자고지를 안읽었으면 추후 예약발송 한다는거임. + // POST_SEND_STATE 업데이트 to "1" + updateSendState(cpSendTargets, epSendTargets, cpSendEgojiTargets, epSendEgojiTargets); - // 전송대상 등기번호 조회 - // 전자고지 아니면 textMakcer - // zipMaker - //qryRegSet stt = 03 으로 업데이트 + //qryRegSet POST_PROC_STT = 03, TG_POST_YN = "Y" 으로 업데이트 + if(!cpSendTargets.isEmpty()) ePostQueryDslRepository.updateSenderRegPostStt(cpQueryFactory, cpSendTargets); + if(!epSendEgojiTargets.isEmpty()) ePostQueryDslRepository.updateGojitPostYn(epQueryFactory, cpSendEgojiTargets); + if(!cpSendTargets.isEmpty()) ePostQueryDslRepository.updateSenderRegPostStt(cpQueryFactory, epSendTargets); + if(!epSendEgojiTargets.isEmpty()) ePostQueryDslRepository.updateGojitPostYn(epQueryFactory, epSendEgojiTargets); + // 서울시는 통합 우편서버를 사용중임. + // 거기다가 ftp 로 텍스트 쏴줘야함. + // 그리고 파일 읽는것도 마찬가지 } @@ -182,11 +252,7 @@ public class EPostService { if(epPrts != null && !epPrts.isEmpty()) { insertPrtResults(epPrts, parseResult, "ep"); } - -// if (delivs != null && !delivs.isEmpty()) insertDelivResults(delivs); -// if (prts != null && !prts.isEmpty()) insertPrtResults(prts); -// if (recvs != null && !recvs.isEmpty()) insertRecvResults(recvs); - + // recv는 조회후 바로 업데이트해서 따로 구조변경이 없음, 때문에 queryDsl안에서 조회후 바로 업데이트 함. } @@ -227,9 +293,9 @@ public class EPostService { } //EPOST_MAKE_RESULT 의 콘키와 등기번호로 조회한 결과가 없으면 - EpostMakeResult epostMakeResult = null; - if(dbKind.equals("cp")) epostMakeResult = cpEPostMakeResultRepository.findByConkeyAndRgstNmbr(); - else epostMakeResult = cpEPostMakeResultRepository.findByConkeyAndRgstNmbr(); + Optional epostMakeResult = null; + if(dbKind.equals("cp")) epostMakeResult = cpEPostMakeResultRepository.findByConKeyAndRgstNmbr(fileResult.getConKey(), fileResult.getRestNmbr()); + else epostMakeResult = epEPostMakeResultRepository.findByConKeyAndRgstNmbr(fileResult.getConKey(), fileResult.getRestNmbr()); //EPOST_MAKE_RESULT 인서트 if(epostMakeResult == null) { @@ -246,10 +312,6 @@ public class EPostService { } } }); - - - - } @@ -366,31 +428,10 @@ public class EPostService { epGojiSendHistRepository.saveAll(cpGojiSendHists); epEpostDelivResultRepository.saveAll(epostDelivResults); } - - } - - private void insertReceiveResults(List recvs) { - - - //고지 업데이트 - - //reg 업데이트 - - - } - - - - - - - - - private Set parseDeptCode(EPostDto.SetInfo setInfo, String kind) { String json = null; if(kind.equals("cp") && setInfo.getCpSetinfo() != null) @@ -440,12 +481,132 @@ public class EPostService { return maxKey; } - private String takeRgstNmbr() { - String regNo = null; + private void cancelOldPost(List cpSendTargetKeys, List epSendTargetKeys) { + + String before30DaysFromNow = LocalDateTime.now().minusDays(30).format(DateTimeFormatter.ofPattern("yyyyMMdd")); + + List oldCpPosts = cpSendTargetKeys.stream() + .filter(post -> post.getRegYmd().compareTo(before30DaysFromNow) < 0) + .toList(); + + List oldEpPosts = epSendTargetKeys.stream() + .filter(post -> post.getRegYmd().compareTo(before30DaysFromNow) < 0) + .toList(); + + if(!oldCpPosts.isEmpty()) ePostQueryDslRepository.updateOldPostToCancel(cpQueryFactory, oldCpPosts); + if(!oldEpPosts.isEmpty()) ePostQueryDslRepository.updateOldPostToCancel(epQueryFactory, oldEpPosts); - return regNo; + } + + private void egojiNonReadTargetsUpdateTrgstNmbr(List cpSendEgojiTargets, List epSendEgojiTargets) { + + List cpEpostSenderDetails = new ArrayList<>(); + List epEpostSenderDetails = new ArrayList<>(); + List cpGojiPrts = new ArrayList<>(); + List epGojiPrts = new ArrayList<>(); + + + + cpSendEgojiTargets.forEach(sendTarget -> { + EpostSenderDetail epostSenderDetail = EpostSenderDetail.builder() + .rgstNmbr(generateTrgstNmbr("cp")) + .conKey(sendTarget.getConKey()) + .recevSeq(sendTarget.getRecevSeq()) // mmcode + .build(); + cpEpostSenderDetails.add(epostSenderDetail); + + CpGojiPrt cpGojiPrt = CpGojiPrt.builder() + .gpRegistNo(generateTrgstNmbr("cp")) + .gpConKey(sendTarget.getConKey()) + .gpMmcode(sendTarget.getRecevSeq()) // mmcode + .build(); + cpGojiPrts.add(cpGojiPrt); + }); + + epSendEgojiTargets.forEach(sendTarget -> { + EpostSenderDetail epostSenderDetail = EpostSenderDetail.builder() + .rgstNmbr(generateTrgstNmbr("cp")) + .conKey(sendTarget.getConKey()) + .recevSeq(sendTarget.getRecevSeq()) // mmcode + .build(); + epEpostSenderDetails.add(epostSenderDetail); + + CpGojiPrt cpGojiPrt = CpGojiPrt.builder() + .gpRegistNo(generateTrgstNmbr("cp")) + .gpConKey(sendTarget.getConKey()) + .gpMmcode(sendTarget.getRecevSeq()) // mmcode + .build(); + epGojiPrts.add(cpGojiPrt); + }); + + if(!cpEpostSenderDetails.isEmpty()) cpEPostSenderDetailRepository.saveAll(cpEpostSenderDetails); + if(!epEpostSenderDetails.isEmpty()) epEPostSenderDetailRepository.saveAll(epEpostSenderDetails); + if(!cpGojiPrts.isEmpty()) cpGojiPrtRepository.saveAll(cpGojiPrts); + if(!epGojiPrts.isEmpty()) epGojiPrtRepository.saveAll(epGojiPrts); + } + + private void updateSendState( + List cpSendTargets, List epSendTargets, + List cpSendEpostTargets, List epSendEpostTargets) { + + List cpEpostSenderDetails = new ArrayList<>(); + List epEpostSenderDetails = new ArrayList<>(); + + // POST_SEND_STATE = '1' + cpSendTargets.forEach(target -> { + EpostSenderDetail epostSenderDetail = EpostSenderDetail.builder() + .postSendState("1") + .conKey(target.getConKey()) + .recevSeq(target.getRecevSeq()) // mmcode + .build(); + cpEpostSenderDetails.add(epostSenderDetail); + }); + epSendTargets.forEach(target -> { + EpostSenderDetail epostSenderDetail = EpostSenderDetail.builder() + .postSendState("1") + .conKey(target.getConKey()) + .recevSeq(target.getRecevSeq()) // mmcode + .build(); + epEpostSenderDetails.add(epostSenderDetail); + }); + cpSendEpostTargets.forEach(target -> { + EpostSenderDetail epostSenderDetail = EpostSenderDetail.builder() + .postSendState("1") + .conKey(target.getConKey()) + .recevSeq(target.getRecevSeq()) // mmcode + .build(); + cpEpostSenderDetails.add(epostSenderDetail); + }); + epSendEpostTargets.forEach(target -> { + EpostSenderDetail epostSenderDetail = EpostSenderDetail.builder() + .postSendState("1") + .conKey(target.getConKey()) + .recevSeq(target.getRecevSeq()) // mmcode + .build(); + epEpostSenderDetails.add(epostSenderDetail); + }); + + if(!cpEpostSenderDetails.isEmpty()) cpEPostSenderDetailRepository.saveAll(cpEpostSenderDetails); + if(!epEpostSenderDetails.isEmpty()) epEPostSenderDetailRepository.saveAll(epEpostSenderDetails); + } + + private String generateTrgstNmbr(String dbKind) { + // select * from EPOST_RGST_NMBR where pcursor= '1' ; + String regiNo = null; + EpostRgstNmbr cpTrgstNmbr = cpEPostRgstNmbr.findByPcursor("1"); + EpostRgstNmbr epTrgstNmbr = epEPostRgstNmbr.findByPcursor("1"); + + if(dbKind.equals("cp")) { + regiNo = cpTrgstNmbr.getRgstNmbr(); + ePostQueryDslRepository.updateTrgstNmbr(cpQueryFactory, cpTrgstNmbr); + } + if(dbKind.equals("ep")) { + regiNo = epTrgstNmbr.getRgstNmbr(); + ePostQueryDslRepository.updateTrgstNmbr(epQueryFactory, epTrgstNmbr); + } + return regiNo; } } diff --git a/src/main/java/com/worker/util/common/CommonUtils.java b/src/main/java/com/worker/util/common/CommonUtils.java index 69af53a..95b852c 100644 --- a/src/main/java/com/worker/util/common/CommonUtils.java +++ b/src/main/java/com/worker/util/common/CommonUtils.java @@ -1,12 +1,16 @@ package com.worker.util.common; import com.worker.util.common.commEnum.DateTimePatternEnum; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; +@Component +@Slf4j public class CommonUtils { /** @@ -50,21 +54,15 @@ public class CommonUtils { - /** - * - * */ - - - - - - - - - /** - * 글자 최대수 제한 - * */ - + // 스캐쥴러 에러 체킹을 위한 펑셔널 인터페이스 로깅 매서드 + public void runStep(String step, Runnable r) { + try { + r.run(); + } catch (Exception e) { + log.error("[{}] 실패", step, e); + throw new RuntimeException(e); + } + } } diff --git a/src/main/java/com/worker/util/zipFileMaker/ZipMaker.java b/src/main/java/com/worker/util/zipFileMaker/ZipMaker.java index 93fc1cb..9586102 100644 --- a/src/main/java/com/worker/util/zipFileMaker/ZipMaker.java +++ b/src/main/java/com/worker/util/zipFileMaker/ZipMaker.java @@ -1,4 +1,99 @@ package com.worker.util.zipFileMaker; +import com.worker.scheduler.epost.dto.EPostDto; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.io.*; +import java.util.*; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +@Component +@Slf4j public class ZipMaker { + + private static final List SUFFIXES = Arrays.asList("A", "B", "C", "D"); + + /** + * CP/EP 디렉토리에서.jpeg 가 존재하는 것만 ZIP에 담는다. + */ + public void generateZipFile(List cpMmCodes, List epMmCodes, EPostDto.SetInfo setInfo) throws IOException { + + Objects.requireNonNull(setInfo, "setInfo is null"); + String cpPath = setInfo.getCpSetinfo().getStrValue5(); + String epPath = setInfo.getEpSetinfo().getStrValue5(); + String cpZipPath = setInfo.getCpSetinfo().getStrValue4(); + String epZipPath = setInfo.getCpSetinfo().getStrValue4(); + + // ZIP 저장 디렉토리 없으면 생성 + File cpZipFile = new File(cpZipPath); + File epZipFile = new File(epZipPath); + + try { + FileOutputStream fosCp = new FileOutputStream(cpZipFile); + FileOutputStream fosEp = new FileOutputStream(epZipFile); + ZipOutputStream zosCp = new ZipOutputStream(fosCp); + ZipOutputStream zosEp = new ZipOutputStream(fosEp); + + // 1) CP + for (String code : safeList(cpMmCodes)) { + List files = findExistingJpegs(cpPath, code); + addFilesToZip(zosCp, files, code + "/"); + } + + // 2) EP + for (String code : safeList(epMmCodes)) { + List files = findExistingJpegs(epPath, code); + addFilesToZip(zosEp, files, code + "/"); + } + } catch(Exception e) { + log.error("ZIP 객체 생성 실패 : " + e.getMessage()); + } + } + + /** baseDir/code + (A|B|C|D).jpeg 중 존재하는 파일만 수집 */ + private List findExistingJpegs(String baseDir, String code) { + List found = new ArrayList<>(); + if (isBlank(baseDir) || isBlank(code)) return found; + + for (String sfx : SUFFIXES) { + String filename = code + sfx + ".jpeg"; + File f = new File(baseDir, filename); + if (f.isFile()) { + found.add(f); // 존재하는 것만 담음 (A/B만 있으면 A,B만) + } + } + return found; + } + + /** ZIP에 파일 추가 */ + private void addFilesToZip(ZipOutputStream zos, List files, String zipFolderPrefix) throws IOException { + for (File file : files) { + String entryName = zipFolderPrefix + file.getName(); + zos.putNextEntry(new ZipEntry(entryName)); + + try (InputStream in = new BufferedInputStream(new FileInputStream(file))) { + byte[] buf = new byte[8192]; + int n; + while ((n = in.read(buf)) != -1) { + zos.write(buf, 0, n); + } + } + zos.closeEntry(); + } + } + + private static List safeList(List list) { + return list == null ? Collections.emptyList() : list; + } + + private static boolean isBlank(String s) { + return s == null || s.trim().isEmpty(); + } + + + + + }