diff --git a/.gitignore b/.gitignore
index 7547fa22..52dbc935 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,3 +33,13 @@ build/
### VS Code ###
.vscode/
+
+### logs ###
+qcast3/
+src/test
+logs/
+
+### Claude Code ###
+# 2026-05-08: .claude 전체 및 docs/ 디렉터리는 로컬 전용으로 git 제외
+.claude/
+docs/
diff --git a/pom.xml b/pom.xml
index 91ffd4b2..694d2a45 100644
--- a/pom.xml
+++ b/pom.xml
@@ -15,6 +15,7 @@
qcast
17
+ 2023.0.2
@@ -26,6 +27,10 @@
org.springframework.boot
spring-boot-starter-web
+
+ org.springframework.cloud
+ spring-cloud-starter-openfeign
+
org.springframework.boot
spring-boot-starter-batch
@@ -103,7 +108,7 @@
org.springdoc
springdoc-openapi-starter-webmvc-ui
- 2.6.0
+ 2.3.0
@@ -160,9 +165,47 @@
1.0.9
+
+
+ org.jsoup
+ jsoup
+ 1.18.1
+
+
+ com.itextpdf
+ html2pdf
+ 4.0.3
+
+
+ com.itextpdf
+ kernel
+ 7.2.5
+
+
+ com.itextpdf
+ layout
+ 7.2.5
+
+
+ com.itextpdf
+ io
+ 7.2.5
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-dependencies
+ ${spring-cloud.version}
+ pom
+ import
+
+
+
+
diff --git a/src/main/java/com/interplug/qcast/batch/BatchMonitorController.java b/src/main/java/com/interplug/qcast/batch/BatchMonitorController.java
new file mode 100644
index 00000000..0f859ca4
--- /dev/null
+++ b/src/main/java/com/interplug/qcast/batch/BatchMonitorController.java
@@ -0,0 +1,108 @@
+package com.interplug.qcast.batch;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.batch.core.*;
+import org.springframework.batch.core.explore.JobExplorer;
+import org.springframework.batch.core.launch.JobOperator;
+import org.springframework.web.bind.annotation.*;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.*;
+
+@RestController
+@RequiredArgsConstructor
+@Slf4j
+public class BatchMonitorController {
+ private final JobExplorer jobExplorer;
+ private final JobOperator jobOperator;
+
+ @GetMapping("/batch/status")
+ public Map getBatchStatus() {
+ Map status = new HashMap<>();
+ String[] jobNames = {"storeAdditionalJob", "priceJob", "materialJob", "bomJob",
+ "businessChargerJob", "adminUserJob", "commonCodeJob",
+ "specialNoteDispItemAdditionalJob", "planConfirmJob", "estimateSyncJob"};
+
+ for (String jobName : jobNames) {
+ status.put(jobName, getJobStatus(jobName));
+ }
+
+ return status;
+ }
+
+ private Map getJobStatus(String jobName) {
+ Map jobStatus = new HashMap<>();
+
+ try {
+ List jobInstances = jobExplorer.findJobInstancesByJobName(jobName, 0, 1);
+ if (jobInstances.isEmpty()) {
+ jobStatus.put("status", "NEVER_EXECUTED");
+ return jobStatus;
+ }
+
+ JobInstance latestJobInstance = jobInstances.get(0);
+ List jobExecutions = jobExplorer.getJobExecutions(latestJobInstance);
+
+ if (!jobExecutions.isEmpty()) {
+ JobExecution latestExecution = jobExecutions.get(0);
+ jobStatus.put("status", latestExecution.getStatus().toString());
+ jobStatus.put("startTime", latestExecution.getStartTime());
+ jobStatus.put("endTime", latestExecution.getEndTime());
+
+ // Duration 계산 (LocalDateTime용)
+ if (latestExecution.getEndTime() != null && latestExecution.getStartTime() != null) {
+ Duration duration = Duration.between(latestExecution.getStartTime(), latestExecution.getEndTime());
+ jobStatus.put("durationSeconds", duration.getSeconds());
+ jobStatus.put("durationMinutes", duration.toMinutes());
+ } else if (latestExecution.getStartTime() != null) {
+ // 실행 중인 경우 현재까지의 경과 시간
+ Duration duration = Duration.between(latestExecution.getStartTime(), LocalDateTime.now());
+ jobStatus.put("runningDurationSeconds", duration.getSeconds());
+ jobStatus.put("runningDurationMinutes", duration.toMinutes());
+ }
+ }
+
+ } catch (Exception e) {
+ jobStatus.put("status", "ERROR");
+ jobStatus.put("error", e.getMessage());
+ }
+
+ return jobStatus;
+ }
+
+ /**
+ * 강제 Job 중지
+ */
+ @PostMapping("/batch/stop/{jobName}")
+ public Map stopJob(@PathVariable String jobName) {
+ Map result = new HashMap<>();
+
+ try {
+ Set runningExecutions = jobOperator.getRunningExecutions(jobName);
+
+ if (runningExecutions.isEmpty()) {
+ result.put("code", "NO_RUNNING_JOBS");
+ result.put("message", "No running executions found for job: " + jobName);
+ return result;
+ }
+
+ List stopResults = new ArrayList<>();
+ for (Long executionId : runningExecutions) {
+ boolean stopped = jobOperator.stop(executionId);
+ stopResults.add("Execution " + executionId + ": " + (stopped ? "STOPPED" : "FAILED_TO_STOP"));
+ }
+
+ result.put("code", "SUCCESS");
+ result.put("message", "Stop commands sent");
+ result.put("results", stopResults);
+
+ } catch (Exception e) {
+ result.put("code", "ERROR");
+ result.put("message", "Error stopping job: " + e.getMessage());
+ }
+
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/interplug/qcast/batch/JobLauncherController.java b/src/main/java/com/interplug/qcast/batch/JobLauncherController.java
index 1e30e270..bcac883b 100644
--- a/src/main/java/com/interplug/qcast/batch/JobLauncherController.java
+++ b/src/main/java/com/interplug/qcast/batch/JobLauncherController.java
@@ -1,27 +1,69 @@
package com.interplug.qcast.batch;
-import java.util.Date;
-import java.util.Map;
+import java.time.Duration;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+
import lombok.RequiredArgsConstructor;
-import org.springframework.batch.core.Job;
-import org.springframework.batch.core.JobParameters;
-import org.springframework.batch.core.JobParametersBuilder;
-import org.springframework.batch.core.JobParametersInvalidException;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.batch.core.*;
+import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
+import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.repository.JobRestartException;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
+import org.springframework.context.event.EventListener;
+import java.time.LocalDateTime;
+import java.sql.SQLException;
@RestController
@RequiredArgsConstructor
+@Slf4j
public class JobLauncherController {
private final Map jobs; // 여러 Job을 주입받도록 변경
private final JobLauncher jobLauncher;
+ private final JobExplorer jobExplorer;
+ private final JobRepository jobRepository; // 생성자 주입을 위해 필드 추가
+
+ @Value("${qsp.master-admin-user-batch-url}")
+ private String qspInterfaceUrl;
+
+ @Value("${spring.profiles.scheduler}")
+ private String scheduler;
+
+ @Value("${server.port:8080}")
+ private int serverPort;
+
+ @Value("${batch.scheduler-port:8080}")
+ private int schedulerPort;
+
+ /**
+ * 서버 시작 시 실행 중(STARTED)인 상태로 남은 Job들을 FAILED 처리
+ */
+ @EventListener(ApplicationReadyEvent.class)
+ public void resetFailedJobs() {
+ log.info("Checking for 'STARTED' jobs to reset after reboot...");
+ for (String jobName : jobs.keySet()) {
+ Set runningExecutions = jobExplorer.findRunningJobExecutions(jobName);
+ for (JobExecution execution : runningExecutions) {
+ execution.setStatus(BatchStatus.FAILED);
+ execution.setExitStatus(ExitStatus.FAILED.addExitDescription("Reset on application startup"));
+ execution.setEndTime(LocalDateTime.now()); // new Date() 대신 LocalDateTime.now() 사용
+ jobRepository.update(execution);
+ log.info("Reset job execution {} for job {}", execution.getId(), jobName);
+ }
+ }
+ }
+
+
/**
* 특정 Job을 매핑으로 실행하는 메소드
@@ -34,118 +76,401 @@ public class JobLauncherController {
* @throws JobRestartException
*/
@GetMapping("/batch/job/{jobName}") // Path Variable로 jobName을 받음
- public String launchJob(@PathVariable String jobName)
- throws JobInstanceAlreadyCompleteException,
- JobExecutionAlreadyRunningException,
- JobParametersInvalidException,
- JobRestartException {
+ public Map launchJob(@PathVariable String jobName)
+ throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException,
+ JobParametersInvalidException, JobRestartException {
+
+ log.info("Manual launch requested for job: {}", jobName);
Job job = jobs.get(jobName);
+ Map resultMap = new HashMap();
if (job == null) {
- return "Job " + jobName + " not found";
+ // return "Job " + jobName + " not found";
+ resultMap.put("code", "FAILED");
+ resultMap.put("message", "Job" + jobName + " not found");
+ return resultMap;
}
- JobParameters jobParameters =
- new JobParametersBuilder()
- .addString("jobName", jobName)
- .addDate("time", new Date())
- .toJobParameters();
-
- jobLauncher.run(job, jobParameters);
-
- return "Job " + jobName + " started";
- }
-
- /**
- * 스케줄러로 Job을 실행하는 메소드
- *
- * @return
- * @throws JobInstanceAlreadyCompleteException
- * @throws JobExecutionAlreadyRunningException
- * @throws JobParametersInvalidException
- * @throws JobRestartException
- */
- @Scheduled(cron = "0 55 23 * * *")
- public String scheduleJobLauncher()
- throws JobInstanceAlreadyCompleteException,
- JobExecutionAlreadyRunningException,
- JobParametersInvalidException,
- JobRestartException {
-
- String jobName = "sampleOtherJob";
- Job job = jobs.get(jobName);
- if (job == null) {
- return "Job " + jobName + " not found";
+ // 실행 중인 Job 확인 (데이터베이스 기반)
+ if (isJobRunning(jobName)) {
+ log.warn("Job {} is already running, skipping execution", jobName);
+ resultMap.put("code", "FAILED");
+ resultMap.put("message", "Job "+ jobName +" is already running, skipping execution");
+ return resultMap;
}
- JobParameters jobParameters =
- new JobParametersBuilder()
- .addString("jobName", jobName)
- .addDate("time", new Date())
- .toJobParameters();
+ try {
+ log.info("Starting job: {}", jobName);
- jobLauncher.run(job, jobParameters);
+ JobParameters jobParameters = new JobParametersBuilder().addString("jobName", jobName)
+ .addDate("time", new Date()).toJobParameters();
- return "Job " + jobName + " started";
+ JobExecution jobExecution = jobLauncher.run(job, jobParameters);
+ log.info("Job {} started with execution id {}", jobName, jobExecution.getId());
+
+ BatchStatus status = jobExecution.getStatus();
+ ExitStatus exitStatus = jobExecution.getExitStatus();
+
+ resultMap.put("code", status.toString());
+ resultMap.put("message", exitStatus.getExitDescription());
+
+ // return "Job " + jobName + " started";
+ return resultMap;
+ } catch (JobExecutionAlreadyRunningException e) {
+ log.warn("Job {} 이 다른 인스턴스에서 이미 실행 중입니다.", jobName);
+ resultMap.put("code", "ALREADY_RUNNING");
+ resultMap.put("message", "Job " + jobName + " is already running on another instance.");
+ return resultMap;
+ } catch (Exception e) {
+ logExceptionDetails("Manual launch failed", jobName, e);
+ if (isDeadlockException(e)) {
+ logDeadlockDetails(jobName, e);
+ resultMap.put("code", "DEADLOCK");
+ resultMap.put("message", "Deadlock detected while executing job " + jobName + ". Please retry.");
+ return resultMap;
+ }
+ resultMap.put("code", "FAILED");
+ resultMap.put("message", e.getMessage());
+ return resultMap;
+ }
}
/**
* Q.CAST 판매점 / 사용자 / 즐겨찾기 / 노출 아이템 동기화 배치
*
- * @return
- * @throws JobInstanceAlreadyCompleteException
- * @throws JobExecutionAlreadyRunningException
- * @throws JobParametersInvalidException
- * @throws JobRestartException
+
*/
- // @Scheduled(cron = "*/5 * * * * *")
- @Scheduled(cron = "0 55 23 * * *")
- public String storeAdditionalInfoJob()
- throws JobInstanceAlreadyCompleteException,
- JobExecutionAlreadyRunningException,
- JobParametersInvalidException,
- JobRestartException {
+ // @Scheduled(cron = "*/5 * * * * *")
+ @Scheduled(cron = "0 50 23 * * *")
+ public String storeAdditionalJob() throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException {
- String jobName = "storeAdditionalJob";
- Job job = jobs.get(jobName);
- if (job == null) {
- return "Job " + jobName + " not found";
- }
- JobParameters jobParameters =
- new JobParametersBuilder().addDate("time", new Date()).toJobParameters();
+ return executeScheduledJob("storeAdditionalJob");
- jobLauncher.run(job, jobParameters);
-
- return "OK";
}
/**
* 아이템 동기화 배치
*
- * @return
- * @throws JobInstanceAlreadyCompleteException
- * @throws JobExecutionAlreadyRunningException
- * @throws JobParametersInvalidException
- * @throws JobRestartException
- */
- @Scheduled(cron = "0 0 0 * * *")
- public String materialJob()
- throws JobInstanceAlreadyCompleteException,
- JobExecutionAlreadyRunningException,
- JobParametersInvalidException,
- JobRestartException {
- String jobName = "materialJob";
+ */
+ @Scheduled(cron = "0 50 04 * * *")
+ public String materialJob() throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException {
+
+ return executeScheduledJob("materialJob");
+
+ }
+
+ /**
+ * BOM 아이템 동기화 배치
+ *
+ * [2026-05-13] 04:20 으로 이동 (estimateSyncJob 과 시간 스왑).
+ * 사유: estimateSyncJob 이 만성 timeout 으로 길어질 수 있어, 새벽 마지막 슬롯(05:20)으로 옮김.
+ * BOM 은 GET 단일 호출(0.7초 수준)이라 슬롯 영향 無.
+ */
+ @Scheduled(cron = "0 20 04 * * *")
+ public String bomJob() throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException {
+
+ return executeScheduledJob("bomJob");
+
+ }
+
+ /**
+ * 영업사원 동기화 배치
+ *
+
+ */
+ @Scheduled(cron = "0 40 03 * * *")
+ public String businessChargerJob() throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException {
+
+ return executeScheduledJob("businessChargerJob");
+
+ }
+
+ /**
+ * 관리자 유저 동기화 배치
+ *
+
+ */
+ @Scheduled(cron = "0 30 01 * * *")
+ public String adminUserJob() throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException {
+
+ return executeScheduledJob("adminUserJob");
+
+ }
+
+ /**
+ * 가격 동기화 배치
+ *
+
+ */
+ @Scheduled(cron = "0 20 00 * * *")
+ public String priceJob() throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException {
+
+ return executeScheduledJob("priceJob");
+
+ }
+
+ /**
+ * 공통코드 M_COMM_H, M_COMM_L 동기화 배치
+ *
+
+ */
+ @Scheduled(cron = "0 10 03 * * *")
+ public String commonCodeJob() throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException {
+
+ return executeScheduledJob("commonCodeJob");
+
+ }
+
+ /**
+ * Q.CAST 견적특이사항 / 아이템 표시, 미표시 동기화 배치
+ *
+
+ */
+ @Scheduled(cron = "0 30 23 * * *")
+ public String specialNoteDispItemAdditionalInfoJob() throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException {
+ return executeScheduledJob("specialNoteDispItemAdditionalJob");
+
+ }
+
+ /**
+ * Plan Confrim 동기화 배치
+ *
+
+ */
+ @Scheduled(cron = "0 05 04 * * *")
+ public String planConfirmJob() throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException {
+
+ return executeScheduledJob("planConfirmJob");
+
+ }
+
+ /**
+ * 견적서 전송 동기화 배치
+ *
+ * [2026-05-13] 05:20 으로 이동 (bomJob 과 시간 스왑).
+ * 사유: 만성 QSP timeout 으로 chunk 분할 송신해도 길어질 수 있어, 새벽 마지막 슬롯에 배치.
+ * 다음 스케줄(23:50 storeAdditionalJob)까지 18시간 버퍼 확보 → 후속 job 침범 위험 제거.
+ * chunk 분할 로직은 EstimateSyncConfiguration 참고.
+ */
+ @Scheduled(cron = "0 20 05 * * *")
+ public String estimateSyncJob() throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException {
+
+ return executeScheduledJob("estimateSyncJob");
+
+ }
+
+ /**
+ * 공통 스케줄러 실행 메소드
+ */
+ private String executeScheduledJob(String jobName) throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException {
+ // 스케줄러 전용 포트 인스턴스에서만 실행 (멀티 인스턴스 데드락 방지)
+ if (serverPort != schedulerPort) {
+ log.debug("스케줄러는 {} 포트 인스턴스에서만 실행됩니다. 현재 포트: {}, Job: {} 스킵", schedulerPort, serverPort, jobName);
+ return "Scheduler runs only on port " + schedulerPort + ". Current port: " + serverPort;
+ }
+
Job job = jobs.get(jobName);
if (job == null) {
+ log.error("Job {} not found", jobName);
return "Job " + jobName + " not found";
}
- JobParameters jobParameters =
- new JobParametersBuilder().addDate("time", new Date()).toJobParameters();
+// if (!"Y".equals(scheduler) &&
+// !"materialJob".equals(jobName) &&
+// !"commonCodeJob".equals(jobName) &&
+// !"specialNoteDispItemAdditionalJob".equals(jobName) &&
+// !"planConfirmJob".equals(jobName) &&
+// !"estimateSyncJob".equals(jobName) &&
+// !"bomJob".equals(jobName)){
+// log.info("Scheduler disabled, skipping job {}", jobName);
+// return "Scheduler disabled";
+// }
- jobLauncher.run(job, jobParameters);
+ // 허용된 작업 목록 정의
+ Set allowedJobs = new HashSet<>(Arrays.asList(
+ "materialJob",
+ "commonCodeJob",
+ "specialNoteDispItemAdditionalJob",
+ "planConfirmJob",
+ "estimateSyncJob",
+ "bomJob",
+ "storeAdditionalJob",
+ "businessChargerJob",
+ "adminUserJob"
+ ));
- return "OK";
+ // 스케줄러가 비활성화되어 있고, 허용된 작업이 아닌 경우
+ if (!"Y".equals(scheduler) && !allowedJobs.contains(jobName)) {
+ log.info("스케줄러가 비활성화되어 작업을 건너뜁니다: {}", jobName);
+ return "Scheduler disabled";
+ }
+
+ // 실행 중인 Job 확인 (데이터베이스 기반)
+ if (isJobRunning(jobName)) {
+ log.warn("Job {} is already running, skipping execution", jobName);
+ return "Job already running";
+ }
+
+// JobParameters jobParameters =
+// new JobParametersBuilder().addDate("time", new Date()).toJobParameters();
+//
+// jobLauncher.run(job, jobParameters);
+//
+// return jobName+ " executed successfully";
+
+ JobParameters jobParameters = new JobParametersBuilder()
+ .addString("jobName", jobName)
+ .addDate("time", new Date())
+ .toJobParameters();
+
+ try {
+ log.info("Job {} 실행 시작", jobName);
+ JobExecution jobExecution = jobLauncher.run(job, jobParameters);
+ log.info("Job {} completed with execution id {} and status {}",
+ jobName, jobExecution.getId(), jobExecution.getExitStatus().getExitCode());
+ return jobName + " executed successfully";
+ } catch (JobExecutionAlreadyRunningException e) {
+ log.warn("Job {} 이 다른 인스턴스에서 이미 실행 중입니다. 스킵합니다.", jobName);
+ return "Job " + jobName + " is already running on another instance. Skipped.";
+ } catch (Exception e) {
+ logExceptionDetails("Scheduled launch failed", jobName, e);
+ if (isDeadlockException(e)) {
+ logDeadlockDetails(jobName, e);
+ return "Deadlock detected while executing job " + jobName + ". Manual retry required.";
+ }
+ log.error("Job {} 실행 실패", jobName, e);
+ return "Error executing job " + jobName + ": " + e.getMessage();
+ }
+ }
+
+ /**
+ * Job 실행 상태 확인
+ */
+ private boolean isJobRunning(String jobName) {
+ try {
+ log.debug("Checking running executions for job: {}", jobName);
+ Set runningExecutions = jobExplorer.findRunningJobExecutions(jobName);
+ if (runningExecutions.isEmpty()) {
+ return false;
+ }
+
+ boolean isActuallyBlocked = false;
+ for (JobExecution execution : runningExecutions) {
+ LocalDateTime startTime = execution.getStartTime();
+ if (startTime == null) continue;
+
+ // 현재 시간과 시작 시간의 차이 계산
+ Duration duration = Duration.between(startTime, LocalDateTime.now());
+ long hours = duration.toHours();
+
+ if (hours >= 1) {
+ // 1시간 이상 경과한 경우 로그 출력 및 DB 강제 업데이트
+ log.warn("Job {} (Execution ID: {}) 가 실행된 지 {}시간이 지났습니다. 상태를 FAILED로 초기화하고 재실행을 시도합니다.",
+ jobName, execution.getId(), hours);
+
+ execution.setStatus(BatchStatus.FAILED);
+ execution.setExitStatus(ExitStatus.FAILED.addExitDescription("Forcefully reset: Running for more than 1 hour"));
+ execution.setEndTime(LocalDateTime.now());
+ jobRepository.update(execution);
+
+ log.info("Job {} 의 이전 실행 상태가 성공적으로 초기화되었습니다.", jobName);
+ } else {
+ // 1시간 미만으로 실행 중인 잡이 있다면 진짜 실행 중인 것으로 간주
+ log.info("Job {} 가 현재 실행 중입니다. (시작 시간: {}, 경과 시간: {}분)",
+ jobName, startTime, duration.toMinutes());
+ isActuallyBlocked = true;
+ }
+ }
+
+ return isActuallyBlocked;
+ } catch (Exception e) {
+ log.error("Job 상태 확인 중 오류 발생: {}", e.getMessage());
+ return false;
+ }
+ }
+
+ private void logExceptionDetails(String context, String jobName, Exception e) {
+ Throwable root = e;
+ while (root.getCause() != null) {
+ root = root.getCause();
+ }
+ if (root instanceof SQLException) {
+ SQLException sqlEx = (SQLException) root;
+ log.error("{} for job {}: SQLState={}, errorCode={}, message={}, sqlChain={}",
+ context, jobName, sqlEx.getSQLState(), sqlEx.getErrorCode(), sqlEx.getMessage(),
+ buildSqlExceptionChain(sqlEx), e);
+ } else {
+ log.error("{} for job {}: {}", context, jobName, root.getMessage(), e);
+ }
+ }
+
+ private void logDeadlockDetails(String jobName, Exception e) {
+ log.error("===== [DEADLOCK] Job: {} 데드락 상세 정보 =====", jobName);
+
+ Throwable current = e;
+ int depth = 0;
+ while (current != null && depth < 10) {
+ log.error("[DEADLOCK] Exception chain [{}]: {} - {}",
+ depth, current.getClass().getName(), current.getMessage());
+
+ if (current instanceof SQLException) {
+ SQLException sqlEx = (SQLException) current;
+ log.error("[DEADLOCK] SQLState: {}, ErrorCode: {}, Message: {}",
+ sqlEx.getSQLState(), sqlEx.getErrorCode(), sqlEx.getMessage());
+
+ // SQLException 체인의 next exception도 확인
+ SQLException nextEx = sqlEx.getNextException();
+ int sqlDepth = 0;
+ while (nextEx != null && sqlDepth < 10) {
+ log.error("[DEADLOCK] SQL NextException [{}]: SQLState={}, ErrorCode={}, Message={}",
+ sqlDepth, nextEx.getSQLState(), nextEx.getErrorCode(), nextEx.getMessage());
+ nextEx = nextEx.getNextException();
+ sqlDepth++;
+ }
+ }
+ current = current.getCause();
+ depth++;
+ }
+
+ log.error("[DEADLOCK] Full stack trace:", e);
+ log.error("===== [DEADLOCK] Job: {} 데드락 상세 정보 끝 =====", jobName);
+ }
+
+ private boolean isDeadlockException(Throwable throwable) {
+ Throwable root = throwable;
+ while (root.getCause() != null) {
+ root = root.getCause();
+ }
+ if (root instanceof SQLException) {
+ SQLException sqlEx = (SQLException) root;
+ if ("40001".equals(sqlEx.getSQLState()) || sqlEx.getErrorCode() == 1205) {
+ return true;
+ }
+ }
+ String message = root.getMessage();
+ if (message == null) return false;
+ String lowerMessage = message.toLowerCase();
+ return lowerMessage.contains("deadlock")
+ || message.contains("デッドロック")
+ || message.contains("교착 상태");
+ }
+
+ private String buildSqlExceptionChain(SQLException sqlEx) {
+ StringBuilder sb = new StringBuilder();
+ SQLException current = sqlEx;
+ int depth = 0;
+ while (current != null && depth < 10) {
+ if (depth > 0) {
+ sb.append(" | ");
+ }
+ sb.append("[").append(current.getClass().getSimpleName())
+ .append(" sqlState=").append(current.getSQLState())
+ .append(" errorCode=").append(current.getErrorCode())
+ .append(" message=").append(current.getMessage()).append("]");
+ current = current.getNextException();
+ depth++;
+ }
+ return sb.toString();
}
}
diff --git a/src/main/java/com/interplug/qcast/batch/estimate/EstimateSyncConfiguration.java b/src/main/java/com/interplug/qcast/batch/estimate/EstimateSyncConfiguration.java
new file mode 100644
index 00000000..d2fe4604
--- /dev/null
+++ b/src/main/java/com/interplug/qcast/batch/estimate/EstimateSyncConfiguration.java
@@ -0,0 +1,174 @@
+package com.interplug.qcast.batch.estimate;
+
+import com.interplug.qcast.biz.estimate.EstimateService;
+import com.interplug.qcast.biz.estimate.dto.EstimateSendRequest;
+import com.interplug.qcast.biz.estimate.dto.EstimateSendResponse;
+import com.interplug.qcast.biz.estimate.dto.EstimateSyncResponse;
+import com.interplug.qcast.biz.estimate.dto.PlanSyncResponse;
+import com.interplug.qcast.util.InterfaceQsp;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.batch.core.Job;
+import org.springframework.batch.core.JobExecutionListener;
+import org.springframework.batch.core.Step;
+import org.springframework.batch.core.configuration.annotation.StepScope;
+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.item.ItemProcessor;
+import org.springframework.batch.item.ItemWriter;
+import org.springframework.batch.item.support.ListItemReader;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
+import org.springframework.transaction.PlatformTransactionManager;
+
+/** Plan 확정 Item 마스터 동기화 배치 */
+@Slf4j
+@Configuration
+public class EstimateSyncConfiguration implements JobExecutionListener {
+ private final EstimateService estimateService;
+
+ private final InterfaceQsp interfaceQsp;
+
+ @Value("${qsp.estimate-sync-batch-url}")
+ private String qspInterfaceUrl;
+
+ // [2026-05-13] QSP 견적 동기화 chunk 크기 — 만성 120s timeout 회피 위해 분할 송신
+ @Value("${qsp.estimate-sync-chunk-size:50}")
+ private int estimateSyncChunkSize;
+
+ // [2026-05-13] 총 송신 시간 상한 (밀리초). 초과 시 남은 chunk 중단하여 다음 스케줄 job (04:50 materialJob) 침범 방지.
+ @Value("${qsp.estimate-sync-total-timeout-ms:1500000}")
+ private long estimateSyncTotalTimeoutMs;
+
+ public EstimateSyncConfiguration(EstimateService estimateService, InterfaceQsp interfaceQsp) {
+ this.estimateService = estimateService;
+ this.interfaceQsp = interfaceQsp;
+ }
+
+ @Bean
+ public Job estimateSyncJob(JobRepository jobRepository, Step estimateSyncStep) {
+ return new JobBuilder("estimateSyncJob", jobRepository).start(estimateSyncStep).build();
+ }
+
+ @Bean
+ public Step estimateSyncStep(
+ JobRepository jobRepository, PlatformTransactionManager transactionManager) throws Exception {
+ return new StepBuilder("estimateSyncStep", jobRepository)
+ .chunk(100, transactionManager)
+ .reader(estimateSyncReader())
+ .processor(estimateSyncProcessor())
+ .writer(estimateSyncWriter())
+ .build();
+ }
+
+ @Bean
+ @StepScope
+ public ListItemReader estimateSyncReader() throws Exception {
+ // [2026-05-13] 만성 timeout 회피: 전체 fail list 를 한 번에 POST 하지 않고 chunk 단위로 분할 송신.
+ // 각 chunk 는 try/catch — 일부 chunk 실패해도 다음 chunk 진행.
+ // 모든 chunk 실패 시에만 throw 하여 Step FAILED.
+ // 총 elapsed 가 상한 초과 시 남은 chunk 중단 (다음 스케줄 job 침범 방지).
+ EstimateSendRequest fullRequest = estimateService.selectEstimateSyncFailList();
+ List quoteList =
+ (fullRequest != null) ? fullRequest.getQuoteList() : null;
+
+ if (quoteList == null || quoteList.isEmpty()) {
+ log.info("QSP 견적 동기화: 송신할 fail list 없음");
+ return new ListItemReader<>(Collections.emptyList());
+ }
+
+ int chunkSize = Math.max(1, estimateSyncChunkSize);
+ int totalChunks = (quoteList.size() + chunkSize - 1) / chunkSize;
+ List mergedSuccessList = new ArrayList<>();
+ int successChunks = 0;
+ int abortedChunks = 0;
+ long startAt = System.currentTimeMillis();
+
+ log.info(
+ "QSP 견적 동기화 분할 송신 시작: totalItems={}, chunkSize={}, totalChunks={}",
+ quoteList.size(),
+ chunkSize,
+ totalChunks);
+
+ for (int i = 0; i < quoteList.size(); i += chunkSize) {
+ long elapsed = System.currentTimeMillis() - startAt;
+ if (elapsed > estimateSyncTotalTimeoutMs) {
+ abortedChunks = totalChunks - (i / chunkSize);
+ log.warn(
+ "QSP 견적 동기화 총 elapsed 상한 초과 — 남은 chunk 중단: elapsedMs={}, limitMs={}, abortedChunks={}",
+ elapsed,
+ estimateSyncTotalTimeoutMs,
+ abortedChunks);
+ break;
+ }
+
+ int end = Math.min(i + chunkSize, quoteList.size());
+ int chunkIndex = i / chunkSize;
+ List sub = new ArrayList<>(quoteList.subList(i, end));
+ EstimateSendRequest subRequest = new EstimateSendRequest();
+ subRequest.setQuoteList(sub);
+
+ try {
+ EstimateSyncResponse chunkResponse =
+ interfaceQsp.callApiData(
+ HttpMethod.POST, qspInterfaceUrl, subRequest, EstimateSyncResponse.class);
+ if (chunkResponse != null && chunkResponse.getSuccessList() != null) {
+ mergedSuccessList.addAll(chunkResponse.getSuccessList());
+ }
+ successChunks++;
+ log.info(
+ "QSP 견적 동기화 chunk 성공: chunkIndex={}/{}, range=[{},{}), accumulatedSuccess={}",
+ chunkIndex,
+ totalChunks,
+ i,
+ end,
+ mergedSuccessList.size());
+ } catch (Exception e) {
+ log.error(
+ "QSP 견적 동기화 chunk 실패 — 다음 chunk 계속: chunkIndex={}/{}, range=[{},{})",
+ chunkIndex,
+ totalChunks,
+ i,
+ end,
+ e);
+ }
+ }
+
+ long totalElapsed = System.currentTimeMillis() - startAt;
+ log.info(
+ "QSP 견적 동기화 분할 송신 완료: totalChunks={}, successChunks={}, abortedChunks={}, mergedSuccess={}, totalElapsedMs={}",
+ totalChunks,
+ successChunks,
+ abortedChunks,
+ mergedSuccessList.size(),
+ totalElapsed);
+
+ if (successChunks == 0) {
+ throw new IllegalStateException(
+ "QSP 견적 동기화 모든 chunk 실패: totalChunks="
+ + totalChunks
+ + ", quoteSize="
+ + quoteList.size());
+ }
+
+ return new ListItemReader<>(mergedSuccessList);
+ }
+
+ @Bean
+ public ItemProcessor estimateSyncProcessor() {
+ return item -> item;
+ }
+
+ @Bean
+ public ItemWriter estimateSyncWriter() {
+ return items -> {
+ List planList = new ArrayList<>(items.getItems());
+ estimateService.setEstimateSyncSave(planList);
+ };
+ }
+}
diff --git a/src/main/java/com/interplug/qcast/batch/estimate/PlanConfrimConfiguration.java b/src/main/java/com/interplug/qcast/batch/estimate/PlanConfrimConfiguration.java
new file mode 100644
index 00000000..36964244
--- /dev/null
+++ b/src/main/java/com/interplug/qcast/batch/estimate/PlanConfrimConfiguration.java
@@ -0,0 +1,81 @@
+package com.interplug.qcast.batch.estimate;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.interplug.qcast.biz.estimate.EstimateService;
+import com.interplug.qcast.biz.estimate.dto.PlanSyncResponse;
+import com.interplug.qcast.util.InterfaceQsp;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.springframework.batch.core.Job;
+import org.springframework.batch.core.JobExecutionListener;
+import org.springframework.batch.core.Step;
+import org.springframework.batch.core.configuration.annotation.StepScope;
+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.item.ItemProcessor;
+import org.springframework.batch.item.ItemWriter;
+import org.springframework.batch.item.support.ListItemReader;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
+import org.springframework.transaction.PlatformTransactionManager;
+
+/** Plan 확정 Item 마스터 동기화 배치 */
+@Configuration
+public class PlanConfrimConfiguration implements JobExecutionListener {
+ private final EstimateService estimateService;
+
+ private final InterfaceQsp interfaceQsp;
+
+ List planSyncList;
+
+ @Value("${qsp.estimate-plan-confirm-batch-url}")
+ private String qspInterfaceUrl;
+
+ public PlanConfrimConfiguration(EstimateService estimateService, InterfaceQsp interfaceQsp) {
+ this.estimateService = estimateService;
+ this.interfaceQsp = interfaceQsp;
+ }
+
+ @Bean
+ public Job planConfirmJob(JobRepository jobRepository, Step planConfirmStep) {
+ return new JobBuilder("planConfirmJob", jobRepository).start(planConfirmStep).build();
+ }
+
+ @Bean
+ public Step planConfirmStep(
+ JobRepository jobRepository, PlatformTransactionManager transactionManager) throws Exception {
+ return new StepBuilder("planConfirmStep", jobRepository)
+ .chunk(100, transactionManager)
+ .reader(planConfirmReader())
+ .processor(planConfirmProcessor())
+ .writer(planConfirmWriter())
+ .build();
+ }
+
+ @Bean
+ @StepScope
+ public ListItemReader planConfirmReader() throws Exception {
+ this.planSyncList =
+ interfaceQsp.callApiData(
+ HttpMethod.GET, qspInterfaceUrl, null, new TypeReference>() {});
+ return (planSyncList != null)
+ ? new ListItemReader<>(planSyncList)
+ : new ListItemReader<>(Collections.emptyList());
+ }
+
+ @Bean
+ public ItemProcessor planConfirmProcessor() {
+ return item -> item;
+ }
+
+ @Bean
+ public ItemWriter planConfirmWriter() {
+ return items -> {
+ estimateService.setPlanConfirmSyncSave(new ArrayList<>(items.getItems()));
+ };
+ }
+}
diff --git a/src/main/java/com/interplug/qcast/batch/master/BomConfiguration.java b/src/main/java/com/interplug/qcast/batch/master/BomConfiguration.java
new file mode 100644
index 00000000..4e3c1ea2
--- /dev/null
+++ b/src/main/java/com/interplug/qcast/batch/master/BomConfiguration.java
@@ -0,0 +1,89 @@
+package com.interplug.qcast.batch.master;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.interplug.qcast.biz.displayItem.DisplayItemService;
+import com.interplug.qcast.biz.displayItem.dto.BomSyncResponse;
+import com.interplug.qcast.util.InterfaceQsp;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import org.springframework.batch.core.Job;
+import org.springframework.batch.core.JobExecutionListener;
+import org.springframework.batch.core.Step;
+import org.springframework.batch.core.configuration.annotation.StepScope;
+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.item.ItemProcessor;
+import org.springframework.batch.item.ItemWriter;
+import org.springframework.batch.item.support.ListItemReader;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
+import org.springframework.transaction.PlatformTransactionManager;
+
+/** Bom Item 마스터 동기화 배치 */
+@Configuration
+public class BomConfiguration implements JobExecutionListener {
+ private final DisplayItemService displayItemService;
+
+ private final InterfaceQsp interfaceQsp;
+
+ List bomSyncList;
+
+ @Value("${qsp.master-bom-batch-url}")
+ private String qspInterfaceUrl;
+
+ public BomConfiguration(DisplayItemService displayItemService, InterfaceQsp interfaceQsp) {
+ this.displayItemService = displayItemService;
+ this.interfaceQsp = interfaceQsp;
+ }
+
+ @Bean
+ public Job bomJob(JobRepository jobRepository, Step bomStep) {
+ return new JobBuilder("bomJob", jobRepository).start(bomStep).build();
+ }
+
+ @Bean
+ public Step bomStep(JobRepository jobRepository, PlatformTransactionManager transactionManager)
+ throws Exception {
+ return new StepBuilder("bomStep", jobRepository)
+ .chunk(100, transactionManager)
+ .reader(bomReader())
+ .processor(bomProcessor())
+ .writer(bomWriter())
+ .build();
+ }
+
+ @Bean
+ @StepScope
+ public ListItemReader bomReader() throws Exception {
+ this.bomSyncList =
+ interfaceQsp.callApiData(
+ HttpMethod.GET, qspInterfaceUrl, null, new TypeReference>() {});
+ if (bomSyncList == null) {
+ return new ListItemReader<>(Collections.emptyList());
+ }
+ // 2026-05-20 청크 경계에서 같은 키 A/D 가 분리되어 A 가 먼저 MERGE 된 뒤
+ // 다음 청크의 D 가 그것을 삭제해버리는 리스크를 차단하기 위해,
+ // 전체 리스트를 statCd='D' 우선으로 정렬한다. (Spring Batch 는 청크를 순차 처리하므로
+ // 모든 D 청크가 끝난 뒤에 A/U 청크가 시작되어 전역적으로 'A 가 이김' 이 보장됨)
+ bomSyncList.sort(
+ Comparator.comparingInt((BomSyncResponse b) -> "D".equals(b.getStatCd()) ? 0 : 1));
+ return new ListItemReader<>(bomSyncList);
+ }
+
+ @Bean
+ public ItemProcessor bomProcessor() {
+ return item -> item;
+ }
+
+ @Bean
+ public ItemWriter bomWriter() {
+ return items -> {
+ displayItemService.setBomSyncSave(new ArrayList<>(items.getItems()));
+ };
+ }
+}
diff --git a/src/main/java/com/interplug/qcast/batch/master/MaterialConfiguration.java b/src/main/java/com/interplug/qcast/batch/master/MaterialConfiguration.java
index c6477823..578d8eed 100644
--- a/src/main/java/com/interplug/qcast/batch/master/MaterialConfiguration.java
+++ b/src/main/java/com/interplug/qcast/batch/master/MaterialConfiguration.java
@@ -2,11 +2,13 @@ package com.interplug.qcast.batch.master;
import com.fasterxml.jackson.core.type.TypeReference;
import com.interplug.qcast.biz.displayItem.DisplayItemService;
-import com.interplug.qcast.biz.displayItem.dto.ItemSyncRequest;
import com.interplug.qcast.biz.displayItem.dto.ItemSyncResponse;
import com.interplug.qcast.util.InterfaceQsp;
+import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecutionListener;
import org.springframework.batch.core.Step;
@@ -15,7 +17,6 @@ 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.item.ItemProcessor;
-import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.support.ListItemReader;
import org.springframework.beans.factory.annotation.Value;
@@ -24,7 +25,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.transaction.PlatformTransactionManager;
-/** chunk 방식의 Job을 생성하는 Configuration */
+/** Item 마스터 동기화 배치 */
@Configuration
public class MaterialConfiguration implements JobExecutionListener {
private final DisplayItemService displayItemService;
@@ -36,6 +37,9 @@ public class MaterialConfiguration implements JobExecutionListener {
@Value("${qsp.master-material-batch-url}")
private String qspInterfaceUrl;
+ @Value("${qsp.master-material-batch-page-size:1000}")
+ private int pageSize;
+
public MaterialConfiguration(DisplayItemService displayItemService, InterfaceQsp interfaceQsp) {
this.displayItemService = displayItemService;
this.interfaceQsp = interfaceQsp;
@@ -59,15 +63,33 @@ public class MaterialConfiguration implements JobExecutionListener {
@Bean
@StepScope
- public ItemReader materialReader() throws Exception {
- ItemSyncRequest itemSyncRequest = new ItemSyncRequest();
- itemSyncRequest.setAllYn("N");
- this.itemSyncList =
- interfaceQsp.callApiData(
- HttpMethod.GET,
- qspInterfaceUrl,
- itemSyncRequest,
- new TypeReference>() {});
+ public ListItemReader materialReader() throws Exception {
+ List aggregated = new ArrayList<>();
+ int page = 1;
+ while (true) {
+ Map params = new HashMap<>();
+ params.put("allYn", "N");
+ params.put("page", page);
+ params.put("size", pageSize);
+
+ List pageItems =
+ interfaceQsp.callApiData(
+ HttpMethod.GET,
+ qspInterfaceUrl,
+ params,
+ new TypeReference>() {});
+
+ if (pageItems == null || pageItems.isEmpty()) {
+ break;
+ }
+
+ aggregated.addAll(pageItems);
+ if (pageItems.size() < pageSize) {
+ break;
+ }
+ page++;
+ }
+ this.itemSyncList = aggregated;
return (itemSyncList != null)
? new ListItemReader<>(itemSyncList)
: new ListItemReader<>(Collections.emptyList());
@@ -81,7 +103,7 @@ public class MaterialConfiguration implements JobExecutionListener {
@Bean
public ItemWriter materialWriter() {
return items -> {
- displayItemService.setItemSyncSave((List) items.getItems());
+ displayItemService.setItemSyncSave(new ArrayList<>(items.getItems()));
};
}
}
diff --git a/src/main/java/com/interplug/qcast/batch/master/PriceJobConfiguration.java b/src/main/java/com/interplug/qcast/batch/master/PriceJobConfiguration.java
new file mode 100644
index 00000000..a57f3181
--- /dev/null
+++ b/src/main/java/com/interplug/qcast/batch/master/PriceJobConfiguration.java
@@ -0,0 +1,154 @@
+package com.interplug.qcast.batch.master;
+
+import com.interplug.qcast.biz.displayItem.DisplayItemService;
+import com.interplug.qcast.biz.displayItem.dto.PriceItemSyncResponse;
+import com.interplug.qcast.biz.displayItem.dto.PriceSyncResponse;
+import com.interplug.qcast.util.InterfaceQsp;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Consumer;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.batch.core.*;
+import org.springframework.batch.core.configuration.annotation.StepScope;
+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.item.ItemReader;
+import org.springframework.batch.item.ItemWriter;
+import org.springframework.batch.item.support.ListItemReader;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
+import org.springframework.transaction.PlatformTransactionManager;
+
+/** Price 동기화 배치 */
+@Configuration
+@Slf4j
+public class PriceJobConfiguration implements JobExecutionListener {
+
+ private final InterfaceQsp interfaceQsp;
+ private final DisplayItemService displayItemService;
+
+ @Value("${qsp.master-price-batch-url}")
+ private String qspInterfaceUrl;
+
+ private PriceSyncResponse priceSyncResponse;
+
+ public PriceJobConfiguration(InterfaceQsp interfaceQsp, DisplayItemService displayItemService) {
+ this.interfaceQsp = interfaceQsp;
+ this.displayItemService = displayItemService;
+ }
+
+ @Override
+ public void beforeJob(JobExecution jobExecution) {
+ log.info("Job 시작: 초기화 메서드 호출 중...");
+ try {
+ this.priceSyncResponse =
+ interfaceQsp.callApiData(
+ HttpMethod.GET, qspInterfaceUrl + "?allYn=N", null, PriceSyncResponse.class);
+ log.info("API 호출 완료, 항목 수: {}", this.priceSyncResponse.getModulePriceList().size());
+ } catch (Exception e) {
+ log.error("priceSyncResponse 갱신 중 오류: {}", e.getMessage());
+ }
+ }
+
+ @Bean
+ public Job priceJob(JobRepository jobRepository, Step modulePriceStep, Step bosPriceStep) {
+ return new JobBuilder("priceJob", jobRepository)
+ .start(modulePriceStep)
+ .next(bosPriceStep)
+ .listener(this)
+ .build();
+ }
+
+ private Step buildStep(
+ String stepName,
+ JobRepository jobRepository,
+ PlatformTransactionManager transactionManager,
+ ItemReader reader,
+ ItemWriter writer) {
+ return new StepBuilder(stepName, jobRepository)
+ .chunk(100, transactionManager)
+ .reader(reader)
+ .writer(writer)
+ .build();
+ }
+
+ @Bean
+ public Step modulePriceStep(
+ JobRepository jobRepository, PlatformTransactionManager transactionManager) {
+ return buildStep(
+ "modulePriceStep",
+ jobRepository,
+ transactionManager,
+ modulePriceListReader(),
+ createWriter(
+ (items) -> {
+ try {
+ log.debug("Module Price batch processing {} items", items.size());
+ displayItemService.setPriceSyncSave(items);
+ log.debug("Successfully processed Module Price batch");
+ } catch (Exception e) {
+ log.error("Error processing Module Price batch: {}", e.getMessage(), e);
+ throw new RuntimeException("Failed to process Module Price batch", e);
+ }
+ },
+ "module price"));
+ }
+
+ @Bean
+ public Step bosPriceStep(
+ JobRepository jobRepository, PlatformTransactionManager transactionManager) {
+ return buildStep(
+ "bosPriceStep",
+ jobRepository,
+ transactionManager,
+ bosPriceListReader(),
+ createWriter(
+ (items) -> {
+ try {
+ log.debug("Bos Price batch processing {} items", items.size());
+ displayItemService.setPriceSyncSave(items);
+ log.debug("Successfully processed Bos Price batch");
+ } catch (Exception e) {
+ log.error("Error processing Bos Price batch: {}", e.getMessage(), e);
+ throw new RuntimeException("Failed to process Bos Price batch", e);
+ }
+ },
+ "bos price"));
+ }
+
+ private ListItemReader createReader(List items, String readerName) {
+ log.info("{}Reader 호출됨...", readerName);
+ return new ListItemReader<>(items != null ? items : Collections.emptyList());
+ }
+
+ @Bean
+ @StepScope
+ public ListItemReader modulePriceListReader() {
+ return createReader(
+ priceSyncResponse != null ? priceSyncResponse.getModulePriceList() : null, "module");
+ }
+
+ @Bean
+ @StepScope
+ public ListItemReader bosPriceListReader() {
+ return createReader(
+ priceSyncResponse != null ? priceSyncResponse.getBosPriceList() : null, "bos");
+ }
+
+ private ItemWriter createWriter(Consumer> processor, String writerName) {
+ return items -> {
+ try {
+ List itemList = new ArrayList<>(items.getItems());
+ processor.accept(itemList);
+ log.info("{}Writer: {} items 처리 완료", writerName, items.size());
+ } catch (Exception e) {
+ log.error("{}Writer 오류: {}", writerName, e.getMessage());
+ throw e;
+ }
+ };
+ }
+}
diff --git a/src/main/java/com/interplug/qcast/batch/master/SpecialNoteDispItemJobConfiguration.java b/src/main/java/com/interplug/qcast/batch/master/SpecialNoteDispItemJobConfiguration.java
new file mode 100644
index 00000000..c00b4304
--- /dev/null
+++ b/src/main/java/com/interplug/qcast/batch/master/SpecialNoteDispItemJobConfiguration.java
@@ -0,0 +1,170 @@
+package com.interplug.qcast.batch.master;
+
+import com.interplug.qcast.biz.specialNote.SpecialNoteService;
+import com.interplug.qcast.biz.specialNote.dto.SpecialNoteItemRequest;
+import com.interplug.qcast.biz.specialNote.dto.SpecialNoteRequest;
+import com.interplug.qcast.biz.specialNote.dto.SpecialNoteSyncResponse;
+import com.interplug.qcast.util.InterfaceQsp;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Consumer;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.batch.core.Job;
+import org.springframework.batch.core.JobExecution;
+import org.springframework.batch.core.JobExecutionListener;
+import org.springframework.batch.core.Step;
+import org.springframework.batch.core.configuration.annotation.StepScope;
+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.item.ItemReader;
+import org.springframework.batch.item.ItemWriter;
+import org.springframework.batch.item.support.ListItemReader;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
+import org.springframework.transaction.PlatformTransactionManager;
+
+@Configuration
+@Slf4j
+public class SpecialNoteDispItemJobConfiguration implements JobExecutionListener {
+
+ private final InterfaceQsp interfaceQsp;
+
+ private final SpecialNoteService specialNoteService;
+
+ @Value("${qsp.master-special-note-disp-item-batch-url}")
+ private String qspMasterSpecialNoteDispItemBatchUrl;
+
+ private SpecialNoteSyncResponse SpecialNoteDispItemSyncResponse;
+
+ public SpecialNoteDispItemJobConfiguration(
+ InterfaceQsp interfaceQsp, SpecialNoteService specialNoteService) {
+ this.interfaceQsp = interfaceQsp;
+ this.specialNoteService = specialNoteService;
+ }
+
+ @Override
+ public void beforeJob(JobExecution jobExecution) {
+ log.info("Job 시작: 초기화 메서드 호출 중...");
+ try {
+ this.SpecialNoteDispItemSyncResponse =
+ interfaceQsp.callApiData(
+ HttpMethod.GET,
+ qspMasterSpecialNoteDispItemBatchUrl,
+ null,
+ SpecialNoteSyncResponse.class);
+ log.info(
+ "API 호출 완료, 항목 수: {}", this.SpecialNoteDispItemSyncResponse.getSdOrderSpnList().size());
+ } catch (Exception e) {
+ log.error("specialNoteDispItemSyncResponse 갱신 중 오류: {}", e.getMessage());
+ }
+ }
+
+ @Bean
+ public Job specialNoteDispItemAdditionalJob(
+ JobRepository jobRepository, Step specialNoteStep, Step specialNoteItemStep) {
+ return new JobBuilder("specialNoteDispItemAdditionalJob", jobRepository)
+ .start(specialNoteStep)
+ .next(specialNoteItemStep)
+ .listener(this)
+ .build();
+ }
+
+ private Step buildStep(
+ String stepName,
+ JobRepository jobRepository,
+ PlatformTransactionManager transactionManager,
+ ItemReader reader,
+ ItemWriter writer) {
+ return new StepBuilder(stepName, jobRepository)
+ .chunk(10, transactionManager)
+ .reader(reader)
+ .writer(writer)
+ .build();
+ }
+
+ @Bean
+ public Step specialNoteStep(
+ JobRepository jobRepository, PlatformTransactionManager transactionManager) {
+ return buildStep(
+ "specialNoteStep",
+ jobRepository,
+ transactionManager,
+ specialNoteListReader(),
+ createWriter(
+ (items) -> {
+ try {
+ log.debug("Special Note batch processing {} items", items.size());
+ specialNoteService.setSpecialNoteBatch(items);
+ log.debug("Successfully processed Special Note batch");
+ } catch (Exception e) {
+ log.error("Error processing Special Note batch: {}", e.getMessage(), e);
+ throw new RuntimeException("Failed to process Special Note batch", e);
+ }
+ },
+ "specialNote"));
+ }
+
+ @Bean
+ public Step specialNoteItemStep(
+ JobRepository jobRepository, PlatformTransactionManager transactionManager) {
+ return buildStep(
+ "specialNoteItemStep",
+ jobRepository,
+ transactionManager,
+ specialNoteItemListReader(),
+ createWriter(
+ (items) -> {
+ try {
+ log.debug("Special Note Item batch processing {} items", items.size());
+ specialNoteService.setSpecialNoteItemBatch(items);
+ log.debug("Successfully processed Special Note Item batch");
+ } catch (Exception e) {
+ log.error("Error processing Special Note Item batch: {}", e.getMessage(), e);
+ throw new RuntimeException("Failed to process Special Note Item batch", e);
+ }
+ },
+ "specialNoteItem"));
+ }
+
+ private ListItemReader createReader(List items, String readerName) {
+ log.info("{}Reader 호출됨...", readerName);
+ return new ListItemReader<>(items != null ? items : Collections.emptyList());
+ }
+
+ @Bean
+ @StepScope
+ public ListItemReader specialNoteListReader() {
+ return createReader(
+ SpecialNoteDispItemSyncResponse != null
+ ? SpecialNoteDispItemSyncResponse.getSdOrderSpnList()
+ : null,
+ "specialNote");
+ }
+
+ @Bean
+ @StepScope
+ public ListItemReader specialNoteItemListReader() {
+ return createReader(
+ SpecialNoteDispItemSyncResponse != null
+ ? SpecialNoteDispItemSyncResponse.getSdOrderSpnItemList()
+ : null,
+ "specialNoteItem");
+ }
+
+ private ItemWriter createWriter(Consumer> processor, String writerName) {
+ return items -> {
+ try {
+ List itemList = new ArrayList<>(items.getItems());
+ processor.accept(itemList);
+ log.info("{}Writer: {} items 처리 완료", writerName, items.size());
+ } catch (Exception e) {
+ log.error("{}Writer 오류: {}", writerName, e.getMessage());
+ throw e;
+ }
+ };
+ }
+}
diff --git a/src/main/java/com/interplug/qcast/batch/master/StoreJobConfiguration.java b/src/main/java/com/interplug/qcast/batch/master/StoreJobConfiguration.java
index 9a733956..0ceb5428 100644
--- a/src/main/java/com/interplug/qcast/batch/master/StoreJobConfiguration.java
+++ b/src/main/java/com/interplug/qcast/batch/master/StoreJobConfiguration.java
@@ -1,12 +1,16 @@
package com.interplug.qcast.batch.master;
+import com.interplug.qcast.biz.displayItem.DisplayItemService;
+import com.interplug.qcast.biz.displayItem.dto.DisplayItemRequest;
import com.interplug.qcast.biz.storeFavorite.StoreFavoriteService;
import com.interplug.qcast.biz.storeFavorite.dto.StoreFavoriteRequest;
import com.interplug.qcast.biz.user.UserService;
import com.interplug.qcast.biz.user.dto.StoreRequest;
import com.interplug.qcast.biz.user.dto.StoreSyncResponse;
+import com.interplug.qcast.biz.user.dto.StoreSyncResquest;
import com.interplug.qcast.biz.user.dto.UserRequest;
import com.interplug.qcast.util.InterfaceQsp;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
@@ -32,6 +36,7 @@ public class StoreJobConfiguration implements JobExecutionListener {
private final InterfaceQsp interfaceQsp;
private final UserService userService;
private final StoreFavoriteService storeFavService;
+ private final DisplayItemService displayItemService;
@Value("${qsp.master-store-batch-url}")
private String qspMasterStoreBatchUrl;
@@ -39,19 +44,26 @@ public class StoreJobConfiguration implements JobExecutionListener {
private StoreSyncResponse storeSyncResponse;
public StoreJobConfiguration(
- InterfaceQsp interfaceQsp, UserService userService, StoreFavoriteService storeFavService) {
+ InterfaceQsp interfaceQsp,
+ UserService userService,
+ StoreFavoriteService storeFavService,
+ DisplayItemService displayItemService) {
this.interfaceQsp = interfaceQsp;
this.userService = userService;
this.storeFavService = storeFavService;
+ this.displayItemService = displayItemService;
}
@Override
public void beforeJob(JobExecution jobExecution) {
log.info("Job 시작: 초기화 메서드 호출 중...");
try {
+ StoreSyncResquest storeSyncResquest = new StoreSyncResquest();
+ storeSyncResquest.setAllYn("N"); // 전체가 아닌 날짜조건으로 조회
+
this.storeSyncResponse =
interfaceQsp.callApiData(
- HttpMethod.GET, qspMasterStoreBatchUrl, null, StoreSyncResponse.class);
+ HttpMethod.GET, qspMasterStoreBatchUrl, storeSyncResquest, StoreSyncResponse.class);
log.info("API 호출 완료, 항목 수: {}", this.storeSyncResponse.getStoreList().size());
} catch (Exception e) {
log.error("storeSyncResponse 갱신 중 오류: {}", e.getMessage());
@@ -60,11 +72,16 @@ public class StoreJobConfiguration implements JobExecutionListener {
@Bean
public Job storeAdditionalJob(
- JobRepository jobRepository, Step storeStep, Step userStep, Step favoriteStep) {
+ JobRepository jobRepository,
+ Step storeStep,
+ Step userStep,
+ Step favoriteStep,
+ Step storeDispItemStep) {
return new JobBuilder("storeAdditionalJob", jobRepository)
.start(storeStep)
.next(userStep)
.next(favoriteStep)
+ .next(storeDispItemStep)
.listener(this)
.build();
}
@@ -93,12 +110,9 @@ public class StoreJobConfiguration implements JobExecutionListener {
createWriter(
(items) -> {
try {
- log.debug("Store batch processing {} items", items.size());
userService.setStoreBatch(items);
- log.debug("Successfully processed store batch");
} catch (Exception e) {
log.error("Error processing store batch: {}", e.getMessage(), e);
- throw new RuntimeException("Failed to process store batch", e);
}
},
"store"));
@@ -114,12 +128,9 @@ public class StoreJobConfiguration implements JobExecutionListener {
createWriter(
(items) -> {
try {
- log.debug("User batch processing {} items", items.size());
userService.setUserBatch(items);
- log.debug("Successfully processed user batch");
} catch (Exception e) {
log.error("Error processing user batch: {}", e.getMessage(), e);
- throw new RuntimeException("Failed to process user batch", e);
}
},
"user"));
@@ -136,17 +147,36 @@ public class StoreJobConfiguration implements JobExecutionListener {
createWriter(
(items) -> {
try {
- log.debug("Favorite batch processing {} items", items.size());
storeFavService.setStoreFavoriteBatch(items);
- log.debug("Successfully processed favorite batch");
} catch (Exception e) {
log.error("Error processing favorite batch: {}", e.getMessage(), e);
- throw new RuntimeException("Failed to process favorite batch", e);
}
},
"favorite"));
}
+ @Bean
+ public Step storeDispItemStep(
+ JobRepository jobRepository, PlatformTransactionManager transactionManager) {
+ return buildStep(
+ "storeDispItemStep",
+ jobRepository,
+ transactionManager,
+ storeDispItemListReader(),
+ createWriter(
+ (items) -> {
+ try {
+ log.debug("Store Disp Item batch processing {} items", items.size());
+ displayItemService.setStoreDispItemBatch(items);
+ log.debug("Successfully processed Store Disp Item batch");
+ } catch (Exception e) {
+ log.error("Error processing Store Disp Item batch: {}", e.getMessage(), e);
+ throw new RuntimeException("Failed to process Store Disp Item batch", e);
+ }
+ },
+ "storeDispItem"));
+ }
+
private ListItemReader createReader(List items, String readerName) {
log.info("{}Reader 호출됨...", readerName);
return new ListItemReader<>(items != null ? items : Collections.emptyList());
@@ -172,10 +202,18 @@ public class StoreJobConfiguration implements JobExecutionListener {
storeSyncResponse != null ? storeSyncResponse.getStoreFavList() : null, "storeFav");
}
+ @Bean
+ @StepScope
+ public ListItemReader storeDispItemListReader() {
+ return createReader(
+ storeSyncResponse != null ? storeSyncResponse.getStoreDispItemList() : null,
+ "storeDispItem");
+ }
+
private ItemWriter createWriter(Consumer> processor, String writerName) {
return items -> {
try {
- List itemList = (List) items.getItems();
+ List itemList = new ArrayList<>(items.getItems());
processor.accept(itemList);
log.info("{}Writer: {} items 처리 완료", writerName, items.size());
} catch (Exception e) {
diff --git a/src/main/java/com/interplug/qcast/batch/system/AdminUserConfiguration.java b/src/main/java/com/interplug/qcast/batch/system/AdminUserConfiguration.java
new file mode 100644
index 00000000..90ad18e8
--- /dev/null
+++ b/src/main/java/com/interplug/qcast/batch/system/AdminUserConfiguration.java
@@ -0,0 +1,89 @@
+package com.interplug.qcast.batch.system;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.interplug.qcast.biz.user.UserService;
+import com.interplug.qcast.biz.user.dto.AdminUserSyncResponse;
+import com.interplug.qcast.util.InterfaceQsp;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.springframework.batch.core.Job;
+import org.springframework.batch.core.JobExecutionListener;
+import org.springframework.batch.core.Step;
+import org.springframework.batch.core.configuration.annotation.StepScope;
+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.item.ItemProcessor;
+import org.springframework.batch.item.ItemWriter;
+import org.springframework.batch.item.support.ListItemReader;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
+import org.springframework.transaction.PlatformTransactionManager;
+
+/** 관리자 유저 동기화 배치 */
+@Configuration
+public class AdminUserConfiguration implements JobExecutionListener {
+ private final UserService userService;
+
+ private final InterfaceQsp interfaceQsp;
+
+ List adminUserSyncList;
+
+ @Value("${qsp.master-admin-user-batch-url}")
+ private String qspInterfaceUrl;
+
+ public AdminUserConfiguration(UserService userService, InterfaceQsp interfaceQsp) {
+ this.userService = userService;
+ this.interfaceQsp = interfaceQsp;
+ }
+
+ @Bean
+ public Job adminUserJob(JobRepository jobRepository, Step adminUserStep) {
+ return new JobBuilder("adminUserJob", jobRepository).start(adminUserStep).build();
+ }
+
+ @Bean
+ public Step adminUserStep(
+ JobRepository jobRepository, PlatformTransactionManager transactionManager) throws Exception {
+ return new StepBuilder("adminUserStep", jobRepository)
+ .chunk(100, transactionManager)
+ .reader(adminUserReader())
+ .processor(adminUserProcessor())
+ .writer(adminUserWriter())
+ .build();
+ }
+
+ @Bean
+ @StepScope
+ public ListItemReader adminUserReader() throws Exception {
+ this.adminUserSyncList =
+ interfaceQsp.callApiData(
+ HttpMethod.GET,
+ qspInterfaceUrl,
+ null,
+ new TypeReference>() {});
+ return (adminUserSyncList != null)
+ ? new ListItemReader<>(adminUserSyncList)
+ : new ListItemReader<>(Collections.emptyList());
+ }
+
+ @Bean
+ public ItemProcessor adminUserProcessor() {
+ return item -> {
+ if (item.getCategory() != null && item.getCategory().length() > 20) {
+ item.setCategory(item.getCategory().substring(0, 20));
+ }
+ return item;
+ };
+ }
+
+ @Bean
+ public ItemWriter adminUserWriter() {
+ return items -> {
+ userService.setAdminUserSyncSave(new ArrayList<>(items.getItems()));
+ };
+ }
+}
diff --git a/src/main/java/com/interplug/qcast/batch/system/BusinessChargerConfiguration.java b/src/main/java/com/interplug/qcast/batch/system/BusinessChargerConfiguration.java
new file mode 100644
index 00000000..df711080
--- /dev/null
+++ b/src/main/java/com/interplug/qcast/batch/system/BusinessChargerConfiguration.java
@@ -0,0 +1,85 @@
+package com.interplug.qcast.batch.system;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.interplug.qcast.biz.user.UserService;
+import com.interplug.qcast.biz.user.dto.BusinessChargerSyncResponse;
+import com.interplug.qcast.util.InterfaceQsp;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.springframework.batch.core.Job;
+import org.springframework.batch.core.JobExecutionListener;
+import org.springframework.batch.core.Step;
+import org.springframework.batch.core.configuration.annotation.StepScope;
+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.item.ItemProcessor;
+import org.springframework.batch.item.ItemWriter;
+import org.springframework.batch.item.support.ListItemReader;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
+import org.springframework.transaction.PlatformTransactionManager;
+
+/** 영업사원 동기화 배치 */
+@Configuration
+public class BusinessChargerConfiguration implements JobExecutionListener {
+ private final UserService userService;
+
+ private final InterfaceQsp interfaceQsp;
+
+ List businessChargerSyncList;
+
+ @Value("${qsp.master-business-charger-batch-url}")
+ private String qspInterfaceUrl;
+
+ public BusinessChargerConfiguration(UserService userService, InterfaceQsp interfaceQsp) {
+ this.userService = userService;
+ this.interfaceQsp = interfaceQsp;
+ }
+
+ @Bean
+ public Job businessChargerJob(JobRepository jobRepository, Step businessChargerStep) {
+ return new JobBuilder("businessChargerJob", jobRepository).start(businessChargerStep).build();
+ }
+
+ @Bean
+ public Step businessChargerStep(
+ JobRepository jobRepository, PlatformTransactionManager transactionManager) throws Exception {
+ return new StepBuilder("businessChargerStep", jobRepository)
+ .chunk(100, transactionManager)
+ .reader(businessChargerReader())
+ .processor(businessChargerProcessor())
+ .writer(businessChargerWriter())
+ .build();
+ }
+
+ @Bean
+ @StepScope
+ public ListItemReader businessChargerReader() throws Exception {
+ this.businessChargerSyncList =
+ interfaceQsp.callApiData(
+ HttpMethod.GET,
+ qspInterfaceUrl,
+ null,
+ new TypeReference>() {});
+ return (businessChargerSyncList != null)
+ ? new ListItemReader<>(businessChargerSyncList)
+ : new ListItemReader<>(Collections.emptyList());
+ }
+
+ @Bean
+ public ItemProcessor
+ businessChargerProcessor() {
+ return item -> item;
+ }
+
+ @Bean
+ public ItemWriter businessChargerWriter() {
+ return items -> {
+ userService.setBusinessChargerSyncSave(new ArrayList<>(items.getItems()));
+ };
+ }
+}
diff --git a/src/main/java/com/interplug/qcast/batch/system/CommonCodeConfiguration.java b/src/main/java/com/interplug/qcast/batch/system/CommonCodeConfiguration.java
new file mode 100644
index 00000000..edcd5b55
--- /dev/null
+++ b/src/main/java/com/interplug/qcast/batch/system/CommonCodeConfiguration.java
@@ -0,0 +1,140 @@
+package com.interplug.qcast.batch.system;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Consumer;
+import org.springframework.batch.core.Job;
+import org.springframework.batch.core.JobExecution;
+import org.springframework.batch.core.JobExecutionListener;
+import org.springframework.batch.core.Step;
+import org.springframework.batch.core.configuration.annotation.StepScope;
+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.item.ItemReader;
+import org.springframework.batch.item.ItemWriter;
+import org.springframework.batch.item.support.ListItemReader;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
+import org.springframework.transaction.PlatformTransactionManager;
+import com.interplug.qcast.biz.commCode.CommCodeService;
+import com.interplug.qcast.biz.commCode.dto.CommonCodeSyncResponse;
+import com.interplug.qcast.biz.commCode.dto.DetailCodeRequest;
+import com.interplug.qcast.biz.commCode.dto.HeadCodeRequest;
+import com.interplug.qcast.util.InterfaceQsp;
+import lombok.extern.slf4j.Slf4j;
+
+/** 공통코드 동기화 배치 */
+@Configuration
+@Slf4j
+public class CommonCodeConfiguration implements JobExecutionListener {
+
+ private final InterfaceQsp interfaceQsp;
+ private final CommCodeService commCodeService;
+
+
+ @Value("${qsp.system-commonCode-batch-url}")
+ private String qspInterfaceUrl;
+
+ private CommonCodeSyncResponse commonCodeSyncResponse;
+
+ public CommonCodeConfiguration(InterfaceQsp interfaceQsp, CommCodeService commCodeService) {
+ this.interfaceQsp = interfaceQsp;
+ this.commCodeService = commCodeService;
+ }
+
+ @Override
+ public void beforeJob(JobExecution jobExecution) {
+ log.info("Job 시작: 초기화 메서드 호출 중...");
+ try {
+ this.commonCodeSyncResponse = interfaceQsp.callApiData(HttpMethod.GET, qspInterfaceUrl, null,
+ CommonCodeSyncResponse.class);
+ log.info("API 호출 완료, 항목 수: {}", this.commonCodeSyncResponse.getApiHeadCdList().size());
+ log.info("API 호출 완료, 항목 수: {}", this.commonCodeSyncResponse.getApiCommCdList().size());
+ } catch (Exception e) {
+ log.error("commonCodeSyncResponse 갱신 중 오류: {}", e.getMessage());
+ }
+ }
+
+ @Bean
+ public Job commonCodeJob(JobRepository jobRepository, Step headCodeStep, Step commonCodeStep) {
+ return new JobBuilder("commonCodeJob", jobRepository).start(headCodeStep).next(commonCodeStep)
+ .listener(this).build();
+ }
+
+ private Step buildStep(String stepName, JobRepository jobRepository,
+ PlatformTransactionManager transactionManager, ItemReader reader, ItemWriter writer) {
+ return new StepBuilder(stepName, jobRepository).chunk(10, transactionManager)
+ .reader(reader).writer(writer).build();
+ }
+
+ @Bean
+ public Step headCodeStep(JobRepository jobRepository,
+ PlatformTransactionManager transactionManager) {
+ return buildStep("headCodeStep", jobRepository, transactionManager, headCodeListReader(),
+ createWriter((items) -> {
+ try {
+ log.debug("HeadCode batch processing {} items", items.size());
+ commCodeService.setHeaderCodeSyncSave(items);
+ log.debug("Successfully processed headCommonCode batch");
+ } catch (Exception e) {
+ log.error("Error processing headCommonCode batch: {}", e.getMessage(), e);
+ throw new RuntimeException("Failed to process headCommonCode batch", e);
+ }
+ }, "headCode"));
+ }
+
+ @Bean
+ public Step commonCodeStep(JobRepository jobRepository,
+ PlatformTransactionManager transactionManager) {
+ return buildStep("commonCodeStep", jobRepository, transactionManager, commonCodeListReader(),
+ createWriter((items) -> {
+ try {
+ log.debug("commonCodeStep batch processing {} items", items.size());
+ commCodeService.setCommonCodeSyncSave(items);
+ log.debug("Successfully processed commonCode batch");
+ } catch (Exception e) {
+ log.error("Error processing commonCode batch: {}", e.getMessage(), e);
+ throw new RuntimeException("Failed to process commonCode batch", e);
+ }
+ }, "commonCode"));
+ }
+
+ private ListItemReader createReader(List items, String readerName) {
+ log.info("{}Reader 호출됨...", readerName);
+ return new ListItemReader<>(items != null ? items : Collections.emptyList());
+ }
+
+ @Bean
+ @StepScope
+ public ListItemReader headCodeListReader() {
+ return createReader(
+ commonCodeSyncResponse != null ? commonCodeSyncResponse.getApiHeadCdList() : null,
+ "headCode");
+ }
+
+ @Bean
+ @StepScope
+ public ListItemReader commonCodeListReader() {
+ return createReader(
+ commonCodeSyncResponse != null ? commonCodeSyncResponse.getApiCommCdList() : null,
+ "commonCode");
+ }
+
+ private ItemWriter createWriter(Consumer> processor, String writerName) {
+ return items -> {
+ try {
+ List itemList = new ArrayList<>(items.getItems());
+ processor.accept(itemList);
+ log.info("{}Writer: {} items 처리 완료", writerName, items.size());
+ } catch (Exception e) {
+ log.error("{}Writer 오류: {}", writerName, e.getMessage());
+ throw e;
+ }
+ };
+ }
+
+}
diff --git a/src/main/java/com/interplug/qcast/biz/MainController.java b/src/main/java/com/interplug/qcast/biz/MainController.java
index 70bdec4b..377214fe 100644
--- a/src/main/java/com/interplug/qcast/biz/MainController.java
+++ b/src/main/java/com/interplug/qcast/biz/MainController.java
@@ -3,6 +3,7 @@ package com.interplug.qcast.biz;
import com.interplug.qcast.config.message.Messages;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -12,11 +13,33 @@ import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/api/main")
public class MainController {
- @Autowired
- Messages message;
+ @Autowired Messages message;
+
+ @Value("${front.url}")
+ private String frontUrl;
@GetMapping
- public String Main() {
- return message.getMessage("example.msg.001");
+ public MainTestResponse Main() {
+ MainTestResponse response =
+ new MainTestResponse(message.getMessage("example.msg.001"), frontUrl);
+ return response;
+ }
+
+ class MainTestResponse {
+ private String message;
+ private String frontUrl;
+
+ public MainTestResponse(String message, String frontUrl) {
+ this.message = message;
+ this.frontUrl = frontUrl;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public String getFrontUrl() {
+ return frontUrl;
+ }
}
}
diff --git a/src/main/java/com/interplug/qcast/biz/canvasBasicSetting/CanvasBasicSettingController.java b/src/main/java/com/interplug/qcast/biz/canvasBasicSetting/CanvasBasicSettingController.java
index 8c3a6c2a..bf469fe2 100644
--- a/src/main/java/com/interplug/qcast/biz/canvasBasicSetting/CanvasBasicSettingController.java
+++ b/src/main/java/com/interplug/qcast/biz/canvasBasicSetting/CanvasBasicSettingController.java
@@ -2,15 +2,14 @@ package com.interplug.qcast.biz.canvasBasicSetting;
import com.interplug.qcast.biz.canvasBasicSetting.dto.CanvasBasicSettingInfo;
import com.interplug.qcast.biz.canvasBasicSetting.dto.CanvasBasicSettingResponse;
-
+import com.interplug.qcast.biz.canvasBasicSetting.dto.RoofAllocationSettingInfo;
+import com.interplug.qcast.config.Exception.QcastException;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-
import java.util.List;
import java.util.Map;
-
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
@@ -21,24 +20,52 @@ import org.springframework.web.bind.annotation.*;
@Tag(name = "CanvasBasicSettingController", description = "Canvas Basic Setting 관련 API")
public class CanvasBasicSettingController {
private final CanvasBasicSettingService canvasBasicSettingService;
-
+
@Operation(description = "Canvas Basic Setting 정보를 조회 한다.")
- @GetMapping("/canvas-basic-settings/by-object/{objectNo}")
- public List selectCanvasBasicSetting(@PathVariable String objectNo) {
+ @GetMapping("/canvas-basic-settings/by-object/{objectNo}/{planNo}")
+ public List selectCanvasBasicSetting(@PathVariable String objectNo, @PathVariable Integer planNo)
+ throws QcastException {
- log.debug("Basic Setting 조회 ::::: " + objectNo);
+ log.debug("Basic Setting 조회 ::::: " + objectNo + " : " + planNo);
- return canvasBasicSettingService.selectCanvasBasicSetting(objectNo);
+ return canvasBasicSettingService.selectCanvasBasicSetting(objectNo, planNo);
}
@Operation(description = "Canvas Basic Setting 정보를 등록 한다.")
@PostMapping("/canvas-basic-settings")
@ResponseStatus(HttpStatus.CREATED)
- public Map insertCanvasBasicSetting(@RequestBody CanvasBasicSettingInfo csi) {
+ public Map insertCanvasBasicSetting(@RequestBody CanvasBasicSettingInfo csi) throws QcastException {
log.debug("Basic Setting 등록 ::::: " + csi.getObjectNo());
return canvasBasicSettingService.insertCanvasBasicSetting(csi);
}
+ @Operation(description = "Canvas Basic Setting 정보를 삭제 한다.")
+ @DeleteMapping("/canvas-basic-settings/delete-basic-settings/{objectNo}/{planNo}")
+ @ResponseStatus(HttpStatus.NO_CONTENT)
+ public void deleteCanvasBasicSetting(@PathVariable String objectNo, @PathVariable Integer planNo) throws QcastException {
+
+ log.debug("Basic Setting 삭제 ::::: " + objectNo + " : " + planNo);
+
+ canvasBasicSettingService.deleteCanvasBasicSetting(objectNo, planNo);
+ }
+
+ @Operation(description = "지붕면 할당 정보를 등록 한다.")
+ @PostMapping("/roof-allocation-settings")
+ @ResponseStatus(HttpStatus.CREATED)
+ public Map insertRoofAllocSetting(@RequestBody RoofAllocationSettingInfo rasi) throws QcastException {
+
+ log.debug("지붕면 할당 등록 ::::: " + rasi.getObjectNo());
+
+ return canvasBasicSettingService.insertRoofAllocSetting(rasi);
+ }
+
+ @Operation(description = "Canvas 지붕재추가 Setting 정보를 삭제 한다.")
+ @DeleteMapping("/canvas-basic-settings/delete-RoofMaterials/{objectNo}/{planNo}")
+ @ResponseStatus(HttpStatus.NO_CONTENT)
+ public void deleteRoofMaterialsAdd(@PathVariable String objectNo, @PathVariable Integer planNo) throws QcastException {
+ canvasBasicSettingService.deleteRoofMaterialsAdd(objectNo, planNo);
+ }
+
}
diff --git a/src/main/java/com/interplug/qcast/biz/canvasBasicSetting/CanvasBasicSettingMapper.java b/src/main/java/com/interplug/qcast/biz/canvasBasicSetting/CanvasBasicSettingMapper.java
index 153fccf7..d456da28 100644
--- a/src/main/java/com/interplug/qcast/biz/canvasBasicSetting/CanvasBasicSettingMapper.java
+++ b/src/main/java/com/interplug/qcast/biz/canvasBasicSetting/CanvasBasicSettingMapper.java
@@ -6,21 +6,42 @@ import org.apache.ibatis.annotations.Mapper;
import com.interplug.qcast.biz.canvasBasicSetting.dto.CanvasBasicSettingInfo;
import com.interplug.qcast.biz.canvasBasicSetting.dto.CanvasBasicSettingResponse;
+import com.interplug.qcast.biz.canvasBasicSetting.dto.RoofAllocationInfo;
import com.interplug.qcast.biz.canvasBasicSetting.dto.RoofMaterialsAddInfo;
@Mapper
public interface CanvasBasicSettingMapper {
- // Canvas Basic Setting 조회(objectNo)
- public List selectCanvasBasicSetting(String objectNo);
-
- // Canvas Basic Setting 등록
- public void insertCanvasBasicSetting(CanvasBasicSettingInfo csi);
-
- // Canvas 지붕재추가 Setting 등록
- public void insertRoofMaterialsAdd(RoofMaterialsAddInfo rma);
+ // Canvas Basic Setting 유무 조회
+ public CanvasBasicSettingInfo getCanvasBasicSettingCnt(String objectNo, Integer planNo);
- // Canvas 지붕재추가 Setting 삭제
- public void deleteRoofMaterialsAdd(String objectNo);
-
+ // Canvas Basic Setting 조회(objectNo)
+ public List selectCanvasBasicSetting(String objectNo, Integer planNo);
+
+ // Canvas Basic Setting 등록
+ public void insertCanvasBasicSetting(CanvasBasicSettingInfo csi);
+
+ // Canvas Basic Setting 수정
+ public void updateCanvasBasicSetting(CanvasBasicSettingInfo csi);
+
+ // Canvas Basic Setting 삭제
+ public void deleteCanvasBasicSetting(String objectNo, Integer planNo);
+
+ // Canvas 지붕재추가 Setting 유무 조회
+ public RoofMaterialsAddInfo getRoofMaterialsCnt(String objectNo, Integer planNo);
+
+ // Canvas 지붕재추가 Setting 등록
+ public void insertRoofMaterialsAdd(RoofMaterialsAddInfo rma);
+
+ // Canvas 지붕재추가 Setting 수정
+ public void updateRoofMaterialsAdd(RoofMaterialsAddInfo rma);
+
+ // 지붕면 할당 Setting 등록
+ public void insertRoofAllocation(RoofAllocationInfo rai);
+
+ // Canvas 지붕재추가 Setting 삭제
+ public void deleteRoofMaterialsAdd(String objectNo, Integer planNo);
+
+ // Canvas 하제비치 update
+ public void updateHajebichRoofMaterialsAdd(CanvasBasicSettingResponse canvasBasicSettingResponse);
}
\ No newline at end of file
diff --git a/src/main/java/com/interplug/qcast/biz/canvasBasicSetting/CanvasBasicSettingService.java b/src/main/java/com/interplug/qcast/biz/canvasBasicSetting/CanvasBasicSettingService.java
index ee5d05a6..6d567d0e 100644
--- a/src/main/java/com/interplug/qcast/biz/canvasBasicSetting/CanvasBasicSettingService.java
+++ b/src/main/java/com/interplug/qcast/biz/canvasBasicSetting/CanvasBasicSettingService.java
@@ -1,61 +1,150 @@
package com.interplug.qcast.biz.canvasBasicSetting;
-import lombok.RequiredArgsConstructor;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.springframework.stereotype.Service;
-
import com.interplug.qcast.biz.canvasBasicSetting.dto.CanvasBasicSettingInfo;
import com.interplug.qcast.biz.canvasBasicSetting.dto.CanvasBasicSettingResponse;
+import com.interplug.qcast.biz.canvasBasicSetting.dto.RoofAllocationInfo;
+import com.interplug.qcast.biz.canvasBasicSetting.dto.RoofAllocationSettingInfo;
import com.interplug.qcast.biz.canvasBasicSetting.dto.RoofMaterialsAddInfo;
+import com.interplug.qcast.config.Exception.ErrorCode;
+import com.interplug.qcast.config.Exception.QcastException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+@Slf4j
@Service
@RequiredArgsConstructor
public class CanvasBasicSettingService {
- private final CanvasBasicSettingMapper canvasBasicSettingMapper;
+ private final CanvasBasicSettingMapper canvasBasicSettingMapper;
- // Canvas Basic Setting 조회(objectNo)
- public List selectCanvasBasicSetting(String objectNo) {
- return canvasBasicSettingMapper.selectCanvasBasicSetting(objectNo);
+//Canvas Basic Setting 조회(objectNo)
+ public List selectCanvasBasicSetting(String objectNo, Integer planNo) throws QcastException {
+ try {
+ return canvasBasicSettingMapper.selectCanvasBasicSetting(objectNo, planNo);
+ } catch (Exception e) {
+ throw new QcastException(ErrorCode.INTERNAL_SERVER_ERROR, e.getMessage());
+ }
}
// Canvas Basic Setting 등록
- public Map insertCanvasBasicSetting(CanvasBasicSettingInfo csi) {
+ public Map insertCanvasBasicSetting(CanvasBasicSettingInfo csi) throws QcastException {
Map response = new HashMap<>();
+
+ if (csi.getObjectNo() == null && csi.getPlanNo() == null) {
+ throw new QcastException (ErrorCode.INVALID_INPUT_VALUE ,"올바르지 않은 입력값입니다.");
+ }
try {
- // 도면/치수/각도 정보 insert/update
- canvasBasicSettingMapper.insertCanvasBasicSetting(csi);
+ // 먼저 데이터가 존재하는지 확인
+ CanvasBasicSettingInfo cntData = canvasBasicSettingMapper.getCanvasBasicSettingCnt(csi.getObjectNo(), csi.getPlanNo());
+
+ log.debug("cntData ::::: " + cntData);
+
+ // 데이터가 존재하지 않으면 insert
+ if (cntData.getRoofCnt().intValue() < 1) {
+ // 도면/치수/각도 정보 insert
+ canvasBasicSettingMapper.insertCanvasBasicSetting(csi);
+
+ // for-each 루프를 사용하여 지붕재추가 Setting
+ for (RoofMaterialsAddInfo rma : csi.getRoofMaterialsAddList()) {
+
+ rma.setObjectNo(csi.getObjectNo());
+ rma.setPlanNo(csi.getPlanNo());
+ // 신규 지붕재추가 정보 insert
+ canvasBasicSettingMapper.insertRoofMaterialsAdd(rma);
+ }
+ response.put("objectNo", csi.getObjectNo());
+ response.put("returnMessage", "common.message.confirm.mark");
+ } else {
+ // 도면/치수/각도 정보 update
+ canvasBasicSettingMapper.updateCanvasBasicSetting(csi);
+
+ // for-each 루프를 사용하여 지붕재추가 Setting
+ for (RoofMaterialsAddInfo rma : csi.getRoofMaterialsAddList()) {
+
+ rma.setObjectNo(csi.getObjectNo());
+ rma.setPlanNo(csi.getPlanNo());
+ // 신규 지붕재추가 정보 insert
+ canvasBasicSettingMapper.updateRoofMaterialsAdd(rma);
+ }
+ response.put("objectNo", csi.getObjectNo());
+ response.put("returnMessage", "common.message.confirm.mark");
+ }
+ } catch (Exception e) {
+ throw new QcastException(ErrorCode.INTERNAL_SERVER_ERROR, e.getMessage());
+ }
+
+ // 생성된 objectNo 반환
+ return response;
+ }
+
+ // Canvas Basic Setting 삭제
+ public void deleteCanvasBasicSetting(String objectNo, Integer planNo) throws QcastException {
+
+ if ((objectNo == null || objectNo.trim().isEmpty()) && (planNo == null)) {
+ throw new QcastException (ErrorCode.INVALID_INPUT_VALUE ,"올바르지 않은 입력값입니다.");
+ }
+
+ try {
+ // Canvas Basic Setting 정보 삭제
+ canvasBasicSettingMapper.deleteCanvasBasicSetting(objectNo, planNo);
+ // 지붕재추가 정보 삭제
+ canvasBasicSettingMapper.deleteRoofMaterialsAdd(objectNo, planNo);
+ } catch (Exception e) {
+ if (e instanceof QcastException) throw e;
+ throw new QcastException(ErrorCode.INTERNAL_SERVER_ERROR, e.getMessage());
+ }
+
+ }
+
+ // 지붕면 할당 Setting 등록
+ public Map insertRoofAllocSetting(RoofAllocationSettingInfo rasi) throws QcastException {
+
+ Map response = new HashMap<>();
+
+ if (rasi.getObjectNo() == null && rasi.getPlanNo() == null) {
+ throw new QcastException (ErrorCode.INVALID_INPUT_VALUE ,"올바르지 않은 입력값입니다.");
+ }
+
+ try {
// 기존 지붕재추가 정보 삭제 후 insert
- canvasBasicSettingMapper.deleteRoofMaterialsAdd(csi.getObjectNo());
+ canvasBasicSettingMapper.deleteRoofMaterialsAdd(rasi.getObjectNo(), rasi.getPlanNo());
- int roofSeq = 1;
- // for-each 루프를 사용하여 지붕재추가 Setting
- for (RoofMaterialsAddInfo rma : csi.getRoofMaterialsAddList()) {
-
- rma.setObjectNo(csi.getObjectNo());
- rma.setRoofSeq(roofSeq++); //roofSeq는 순차적으로 새로 생성하여 insert
-
- // 신규 지붕재추가 정보 insert
- canvasBasicSettingMapper.insertRoofMaterialsAdd(rma);
+ // for-each 루프를 사용하여 지붕재추가 Setting
+ for (RoofAllocationInfo rai : rasi.getRoofAllocationList()) {
+ rai.setObjectNo(rasi.getObjectNo());
+ rai.setPlanNo(rasi.getPlanNo());
+ canvasBasicSettingMapper.insertRoofAllocation(rai);
}
- response.put("objectNo", csi.getObjectNo());
+ response.put("objectNo", rasi.getObjectNo());
response.put("returnMessage", "common.message.confirm.mark");
-
} catch (Exception e) {
- response.put("objectNo", csi.getObjectNo());
- response.put("returnMessage", "common.message.save.error");
+ throw new QcastException(ErrorCode.INTERNAL_SERVER_ERROR, e.getMessage());
}
// 생성된 objectNo 반환
return response;
}
-
+
+ // 지붕재추가 삭제
+ public void deleteRoofMaterialsAdd(String objectNo, Integer planNo) throws QcastException {
+
+ if ((objectNo == null || objectNo.trim().isEmpty()) && (planNo == null)) {
+ throw new QcastException (ErrorCode.INVALID_INPUT_VALUE ,"올바르지 않은 입력값입니다.");
+ }
+
+ try {
+ canvasBasicSettingMapper.deleteRoofMaterialsAdd(objectNo, planNo);
+ } catch (Exception e) {
+ if (e instanceof QcastException) throw e;
+ throw new QcastException(ErrorCode.INTERNAL_SERVER_ERROR, e.getMessage());
+ }
+ }
}
diff --git a/src/main/java/com/interplug/qcast/biz/canvasBasicSetting/dto/CanvasBasicSettingInfo.java b/src/main/java/com/interplug/qcast/biz/canvasBasicSetting/dto/CanvasBasicSettingInfo.java
index c4ba2b9f..08bb8ae6 100644
--- a/src/main/java/com/interplug/qcast/biz/canvasBasicSetting/dto/CanvasBasicSettingInfo.java
+++ b/src/main/java/com/interplug/qcast/biz/canvasBasicSetting/dto/CanvasBasicSettingInfo.java
@@ -11,8 +11,10 @@ import lombok.Setter;
public class CanvasBasicSettingInfo {
private String objectNo; //견적서 번호
- private int roofSizeSet; //치수(복사도/실측값/육지붕)
+ private Integer planNo; // plan 번호
+ private Integer roofSizeSet; //치수(복사도/실측값/육지붕)
private String roofAngleSet; //각도(경사/각도)
+ private Integer roofCnt; //존재여부
private Date registDatetime; //생성일시
private Date lastEditDatetime; //수정일시
diff --git a/src/main/java/com/interplug/qcast/biz/canvasBasicSetting/dto/CanvasBasicSettingResponse.java b/src/main/java/com/interplug/qcast/biz/canvasBasicSetting/dto/CanvasBasicSettingResponse.java
index 9de8b1b8..e13701c5 100644
--- a/src/main/java/com/interplug/qcast/biz/canvasBasicSetting/dto/CanvasBasicSettingResponse.java
+++ b/src/main/java/com/interplug/qcast/biz/canvasBasicSetting/dto/CanvasBasicSettingResponse.java
@@ -6,16 +6,19 @@ import lombok.Setter;
@Getter
@Setter
public class CanvasBasicSettingResponse {
-
- private String objectNo; //견적서 번호
- private int roofDrawingSet; //도면(치수)
- private int roofSizeSet; //치수(복사도/실측값/육지붕)
- private String roofAngleSet; //각도(경사/각도)
- private int roofSeq; //순번 SEQ
- private int roofType; //타입
- private int roofWidth; //넓이
- private int roofHeight; //높이
- private int roofGap; //간격
- private String roofLayout; //방식
-
-}
\ No newline at end of file
+
+ private String objectNo; // 견적서 번호
+ private Integer planNo; // plan 번호
+ private Integer roofSizeSet; // 치수(복사도/실측값/육지붕)
+ private String roofAngleSet; // 각도(경사/각도)
+ private boolean roofApply; // 적용
+ private Integer roofSeq; // 순번 SEQ
+ private String roofMatlCd; // 타입
+ private Integer roofWidth; // 넓이
+ private Integer roofHeight; // 높이
+ private Integer roofHajebichi; // 하제비치
+ private String roofGap; // 간격
+ private String roofLayout; // 방식
+ private Float roofPitch; // 경사도
+ private Float roofAngle; // 각도
+}
diff --git a/src/main/java/com/interplug/qcast/biz/canvasBasicSetting/dto/RoofAllocationInfo.java b/src/main/java/com/interplug/qcast/biz/canvasBasicSetting/dto/RoofAllocationInfo.java
new file mode 100644
index 00000000..5b9d0714
--- /dev/null
+++ b/src/main/java/com/interplug/qcast/biz/canvasBasicSetting/dto/RoofAllocationInfo.java
@@ -0,0 +1,27 @@
+package com.interplug.qcast.biz.canvasBasicSetting.dto;
+
+import java.sql.Date;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class RoofAllocationInfo {
+
+ private String objectNo; // 견적서 번호
+ private Integer planNo; // plan 번호
+ private boolean roofApply; // 적용
+ private Integer roofSeq; // 순번 SEQ
+ private String roofMatlCd; // 타입
+ private Integer roofWidth; // 넓이
+ private Integer roofHeight; // 높이
+ private Integer roofHajebichi;// 하제비치
+ private String roofGap; // 간격
+ private String roofLayout; // 방식
+ private Float roofPitch; // 경사도
+ private Float roofAngle; // 각도
+ private Date registDatetime; // 생성일시
+ private Date lastEditDatetime;// 수정일시
+ private String returnMessage; // return message
+}
diff --git a/src/main/java/com/interplug/qcast/biz/canvasBasicSetting/dto/RoofAllocationSettingInfo.java b/src/main/java/com/interplug/qcast/biz/canvasBasicSetting/dto/RoofAllocationSettingInfo.java
new file mode 100644
index 00000000..73a3c767
--- /dev/null
+++ b/src/main/java/com/interplug/qcast/biz/canvasBasicSetting/dto/RoofAllocationSettingInfo.java
@@ -0,0 +1,18 @@
+package com.interplug.qcast.biz.canvasBasicSetting.dto;
+
+import java.util.List;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class RoofAllocationSettingInfo {
+
+ private String objectNo; //견적서 번호
+ private Integer planNo; // plan 번호
+ private Integer roofSizeSet; //치수(복사도/실측값/육지붕)
+ private String roofAngleSet; //각도(경사/각도)
+
+ private List roofAllocationList;
+}
\ No newline at end of file
diff --git a/src/main/java/com/interplug/qcast/biz/canvasBasicSetting/dto/RoofMaterialsAddInfo.java b/src/main/java/com/interplug/qcast/biz/canvasBasicSetting/dto/RoofMaterialsAddInfo.java
index 61c85496..0d3a233e 100644
--- a/src/main/java/com/interplug/qcast/biz/canvasBasicSetting/dto/RoofMaterialsAddInfo.java
+++ b/src/main/java/com/interplug/qcast/biz/canvasBasicSetting/dto/RoofMaterialsAddInfo.java
@@ -8,17 +8,21 @@ import lombok.Setter;
@Getter
@Setter
public class RoofMaterialsAddInfo {
-
- private String objectNo; //견적서 번호
- private boolean roofApply; //적용
- private int roofSeq; //순번 SEQ
- private int roofType; //타입
- private int roofWidth; //넓이
- private int roofHeight; //높이
- private int roofHajebichi; //하제비치
- private int roofGap; //간격
- private String roofLayout; //방식
- private Date registDatetime; //생성일시
- private Date lastEditDatetime; //수정일시
-
+
+ private String objectNo; // 견적서 번호
+ private Integer planNo; // plan 번호
+ private boolean roofApply; // 적용
+ private Integer roofSeq; // 순번 SEQ
+ private String roofMatlCd; // 타입
+ private Integer roofWidth; // 넓이
+ private Integer roofHeight; // 높이
+ private Integer roofHajebichi;// 하제비치
+ private String roofGap; // 간격
+ private String roofLayout; // 방식
+ private Float roofPitch; // 경사도
+ private Float roofAngle; // 각도
+ private Integer roofCnt; // 존재여부
+ private Date registDatetime; // 생성일시
+ private Date lastEditDatetime;// 수정일시
+ private String returnMessage; // return message
}
diff --git a/src/main/java/com/interplug/qcast/biz/canvasGridSetting/CanvasGridSettingController.java b/src/main/java/com/interplug/qcast/biz/canvasGridSetting/CanvasGridSettingController.java
deleted file mode 100644
index 6d9ce950..00000000
--- a/src/main/java/com/interplug/qcast/biz/canvasGridSetting/CanvasGridSettingController.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package com.interplug.qcast.biz.canvasGridSetting;
-
-import com.interplug.qcast.biz.canvasGridSetting.dto.CanvasGridSettingInfo;
-
-import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.tags.Tag;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-
-import java.util.Map;
-
-import org.springframework.http.HttpStatus;
-import org.springframework.web.bind.annotation.*;
-
-@Slf4j
-@RestController
-@RequestMapping("/api/canvas-management")
-@RequiredArgsConstructor
-@Tag(name = "CanvasGridSettingController", description = "Canvas Grid Setting 관련 API")
-public class CanvasGridSettingController {
- private final CanvasGridSettingService canvasGridSettingService;
-
- @Operation(description = "Canvas Grid Setting 정보를 조회 한다.")
- @GetMapping("/canvas-grid-settings/by-object/{objectNo}")
- public CanvasGridSettingInfo selectCanvasGridSetting(@PathVariable String objectNo) {
-
- log.debug("Grid Setting 조회 ::::: " + objectNo);
-
- return canvasGridSettingService.selectCanvasGridSetting(objectNo);
- }
-
- @Operation(description = "Canvas Grid Setting 정보를 등록 한다.")
- @PostMapping("/canvas-grid-settings")
- @ResponseStatus(HttpStatus.CREATED)
- public Map insertCanvasGridStatus(@RequestBody CanvasGridSettingInfo csi) {
-
- log.debug("Grid Setting 등록 ::::: " + csi.getObjectNo());
-
- return canvasGridSettingService.insertCanvasGridSetting(csi);
- }
-
-}
diff --git a/src/main/java/com/interplug/qcast/biz/canvasGridSetting/CanvasGridSettingMapper.java b/src/main/java/com/interplug/qcast/biz/canvasGridSetting/CanvasGridSettingMapper.java
deleted file mode 100644
index df23ad02..00000000
--- a/src/main/java/com/interplug/qcast/biz/canvasGridSetting/CanvasGridSettingMapper.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.interplug.qcast.biz.canvasGridSetting;
-
-import com.interplug.qcast.biz.canvasGridSetting.dto.CanvasGridSettingInfo;
-
-import org.apache.ibatis.annotations.Mapper;
-
-@Mapper
-public interface CanvasGridSettingMapper {
-
- // Canvas Grid Setting 조회(objectNo)
- public CanvasGridSettingInfo selectCanvasGridSetting(String objectNo);
-
- // Canvas Grid Setting 등록
- public void insertCanvasGridSetting(CanvasGridSettingInfo csi);
-
-}
\ No newline at end of file
diff --git a/src/main/java/com/interplug/qcast/biz/canvasGridSetting/CanvasGridSettingService.java b/src/main/java/com/interplug/qcast/biz/canvasGridSetting/CanvasGridSettingService.java
deleted file mode 100644
index 89fee56d..00000000
--- a/src/main/java/com/interplug/qcast/biz/canvasGridSetting/CanvasGridSettingService.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.interplug.qcast.biz.canvasGridSetting;
-
-import com.interplug.qcast.biz.canvasGridSetting.dto.CanvasGridSettingInfo;
-
-import lombok.RequiredArgsConstructor;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.springframework.stereotype.Service;
-
-@Service
-@RequiredArgsConstructor
-public class CanvasGridSettingService {
- private final CanvasGridSettingMapper canvasGridSettingMapper;
-
- // Canvas Setting 조회(objectNo)
- public CanvasGridSettingInfo selectCanvasGridSetting(String objectNo) {
- return canvasGridSettingMapper.selectCanvasGridSetting(objectNo);
- }
-
- // Canvas Setting 등록
- public Map insertCanvasGridSetting(CanvasGridSettingInfo csi) {
-
- Map response = new HashMap<>();
-
- try {
- canvasGridSettingMapper.insertCanvasGridSetting(csi);
-
- response.put("objectNo", csi.getObjectNo());
- response.put("returnMessage", "common.message.confirm.mark");
-
- } catch (Exception e) {
- response.put("objectNo", csi.getObjectNo());
- response.put("returnMessage", "common.message.save.error");
- }
-
- // 생성된 objectNo 반환
- return response;
-
- }
-
-}
diff --git a/src/main/java/com/interplug/qcast/biz/canvasGridSetting/dto/CanvasGridSettingInfo.java b/src/main/java/com/interplug/qcast/biz/canvasGridSetting/dto/CanvasGridSettingInfo.java
deleted file mode 100644
index 41561633..00000000
--- a/src/main/java/com/interplug/qcast/biz/canvasGridSetting/dto/CanvasGridSettingInfo.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.interplug.qcast.biz.canvasGridSetting.dto;
-
-import java.sql.Date;
-
-import lombok.Getter;
-import lombok.Setter;
-
-@Getter
-@Setter
-public class CanvasGridSettingInfo {
-
- private String objectNo; //견적서 번호
- private boolean dotGridDisplay; //점 그리드 표시
- private boolean lineGridDisplay; //선 그리드 표시
- private Integer gridType; //그리드 설정 타입
- private Integer gridHorizon; //가로 간격
- private Integer gridVertical; //세로 간격
- private Integer gridRatio; //비율 간격
- private String gridDimen; //치수 간격
- private Date registDatetime; //생성일시
- private Date lastEditDatetime; //수정일시
- private String returnMessage; //return message
-
-}
diff --git a/src/main/java/com/interplug/qcast/biz/canvasSetting/CanvasSettingController.java b/src/main/java/com/interplug/qcast/biz/canvasSetting/CanvasSettingController.java
index aa3a4037..c2cc8193 100644
--- a/src/main/java/com/interplug/qcast/biz/canvasSetting/CanvasSettingController.java
+++ b/src/main/java/com/interplug/qcast/biz/canvasSetting/CanvasSettingController.java
@@ -1,14 +1,12 @@
package com.interplug.qcast.biz.canvasSetting;
import com.interplug.qcast.biz.canvasSetting.dto.CanvasSettingInfo;
-
+import com.interplug.qcast.config.Exception.QcastException;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
+import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-
-import java.util.Map;
-
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
@@ -19,33 +17,34 @@ import org.springframework.web.bind.annotation.*;
@Tag(name = "CanvasSettingController", description = "Canvas Setting 관련 API")
public class CanvasSettingController {
private final CanvasSettingService canvasSettingService;
-
+
@Operation(description = "Canvas Setting 정보를 조회 한다.")
@GetMapping("/canvas-settings/by-object/{objectNo}")
- public CanvasSettingInfo selectCanvasSetting(@PathVariable String objectNo) {
-
- log.debug("Setting 조회 ::::: " + objectNo);
-
- return canvasSettingService.selectCanvasSetting(objectNo);
+ public CanvasSettingInfo selectCanvasSetting(@PathVariable String objectNo)
+ throws QcastException {
+
+ log.debug("Setting 조회 ::::: " + objectNo);
+
+ return canvasSettingService.selectCanvasSetting(objectNo);
}
-
+
@Operation(description = "Canvas Setting 정보를 등록 한다.")
@PostMapping("/canvas-settings")
@ResponseStatus(HttpStatus.CREATED)
- public Map insertCanvasSetting(@RequestBody CanvasSettingInfo csi) {
-
- log.debug("Setting 등록 ::::: " + csi.getObjectNo());
-
- return canvasSettingService.insertCanvasSetting(csi);
+ public Map insertCanvasSetting(@RequestBody CanvasSettingInfo csi)
+ throws QcastException {
+
+ log.debug("Setting 등록 ::::: " + csi.getObjectNo());
+
+ return canvasSettingService.insertCanvasSetting(csi);
}
@Operation(description = "Canvas Setting 정보를 수정 한다.")
@PutMapping("/canvas-settings")
- public void updateCanvasStatus(@RequestBody CanvasSettingInfo csi) {
-
- log.debug("Setting 수정 ::::: " + csi.getObjectNo());
-
- canvasSettingService.updateCanvasSetting(csi);
+ public void updateCanvasStatus(@RequestBody CanvasSettingInfo csi) throws QcastException {
+
+ log.debug("Setting 수정 ::::: " + csi.getObjectNo());
+
+ canvasSettingService.updateCanvasSetting(csi);
}
-
}
diff --git a/src/main/java/com/interplug/qcast/biz/canvasSetting/CanvasSettingMapper.java b/src/main/java/com/interplug/qcast/biz/canvasSetting/CanvasSettingMapper.java
index f4b6a2d2..02f36942 100644
--- a/src/main/java/com/interplug/qcast/biz/canvasSetting/CanvasSettingMapper.java
+++ b/src/main/java/com/interplug/qcast/biz/canvasSetting/CanvasSettingMapper.java
@@ -7,6 +7,9 @@ import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface CanvasSettingMapper {
+ // Canvas Setting 유무 조회
+ public CanvasSettingInfo getCanvasSettingCnt(String objectNo);
+
// Canvas Setting 조회(objectNo)
public CanvasSettingInfo selectCanvasSetting(String objectNo);
diff --git a/src/main/java/com/interplug/qcast/biz/canvasSetting/CanvasSettingService.java b/src/main/java/com/interplug/qcast/biz/canvasSetting/CanvasSettingService.java
index eaa18b4e..991e6247 100644
--- a/src/main/java/com/interplug/qcast/biz/canvasSetting/CanvasSettingService.java
+++ b/src/main/java/com/interplug/qcast/biz/canvasSetting/CanvasSettingService.java
@@ -1,48 +1,63 @@
package com.interplug.qcast.biz.canvasSetting;
import com.interplug.qcast.biz.canvasSetting.dto.CanvasSettingInfo;
-
-import lombok.RequiredArgsConstructor;
-
+import com.interplug.qcast.config.Exception.ErrorCode;
+import com.interplug.qcast.config.Exception.QcastException;
import java.util.HashMap;
import java.util.Map;
-
+import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class CanvasSettingService {
- private final CanvasSettingMapper canvasSettingMapper;
+ private final CanvasSettingMapper canvasSettingMapper;
- // Canvas Setting 조회(objectNo)
- public CanvasSettingInfo selectCanvasSetting(String objectNo) {
- return canvasSettingMapper.selectCanvasSetting(objectNo);
- }
+ // Canvas Setting 조회(objectNo)
+ public CanvasSettingInfo selectCanvasSetting(String objectNo) throws QcastException {
+ try {
+ return canvasSettingMapper.selectCanvasSetting(objectNo);
+ } catch (Exception e) {
+ throw new QcastException(ErrorCode.INTERNAL_SERVER_ERROR, e.getMessage());
+ }
+ }
- // Canvas Setting 등록
- public Map insertCanvasSetting(CanvasSettingInfo csi) {
-
- Map response = new HashMap<>();
+ // Canvas Setting 등록
+ public Map insertCanvasSetting(CanvasSettingInfo csi) throws QcastException {
- try {
- canvasSettingMapper.insertCanvasSetting(csi);
-
- response.put("objectNo", csi.getObjectNo());
- response.put("returnMessage", "common.message.confirm.mark");
-
- } catch (Exception e) {
- response.put("objectNo", csi.getObjectNo());
- response.put("returnMessage", "common.message.save.error");
- }
+ Map response = new HashMap<>();
- // 생성된 objectNo 반환
- return response;
+ if (csi.getObjectNo() == null) {
+ throw new QcastException(ErrorCode.INVALID_INPUT_VALUE, "올바르지 않은 입력값입니다.");
+ }
- }
+ try {
- // Canvas Setting 수정
- public void updateCanvasSetting(CanvasSettingInfo csi) {
- canvasSettingMapper.updateCanvasSetting(csi);
- }
+ // 먼저 데이터가 존재하는지 확인
+ CanvasSettingInfo cntData = canvasSettingMapper.getCanvasSettingCnt(csi.getObjectNo());
+ // 데이터가 존재하지 않으면 insert
+ if (cntData.getCnt().intValue() < 1) {
+ canvasSettingMapper.insertCanvasSetting(csi);
+ } else {
+ canvasSettingMapper.updateCanvasSetting(csi);
+ }
+ response.put("objectNo", csi.getObjectNo());
+ response.put("returnMessage", "common.message.confirm.mark");
+ } catch (Exception e) {
+ throw new QcastException(ErrorCode.INTERNAL_SERVER_ERROR, e.getMessage());
+ }
+
+ // 생성된 objectNo 반환
+ return response;
+ }
+
+ // Canvas Setting 수정
+ public void updateCanvasSetting(CanvasSettingInfo csi) throws QcastException {
+ try {
+ canvasSettingMapper.updateCanvasSetting(csi);
+ } catch (Exception e) {
+ throw new QcastException(ErrorCode.INTERNAL_SERVER_ERROR, e.getMessage());
+ }
+ }
}
diff --git a/src/main/java/com/interplug/qcast/biz/canvasSetting/dto/CanvasSettingInfo.java b/src/main/java/com/interplug/qcast/biz/canvasSetting/dto/CanvasSettingInfo.java
index d3b84b6f..a578af9b 100644
--- a/src/main/java/com/interplug/qcast/biz/canvasSetting/dto/CanvasSettingInfo.java
+++ b/src/main/java/com/interplug/qcast/biz/canvasSetting/dto/CanvasSettingInfo.java
@@ -31,8 +31,40 @@ public class CanvasSettingInfo {
private boolean adsorpRangeMedium; //흡착범위설정 중
private boolean adsorpRangeLarge; //흡착범위설정 대
private boolean adsorpPoint; //흡착점 ON OFF
+ private boolean dotGridDisplay;
+ private boolean lineGridDisplay;
+ private Integer gridType;
+ private float gridHorizon;
+ private float gridVertical;
+ private float gridRatio;
+ private String gridDimen;
+ private String gridColor;
+ private String wordFont;
+ private String wordFontStyle;
+ private Integer wordFontSize;
+ private String wordFontColor;
+ private String flowFont;
+ private String flowFontStyle;
+ private Integer flowFontSize;
+ private String flowFontColor;
+ private String dimensioFont;
+ private String dimensioFontStyle;
+ private Integer dimensioFontSize;
+ private String dimensioFontColor;
+ private String circuitNumFont;
+ private String circuitNumFontStyle;
+ private Integer circuitNumFontSize;
+ private String circuitNumFontColor;
+ private String lengthFont;
+ private String lengthFontStyle;
+ private Integer lengthFontSize;
+ private String lengthFontColor;
+ private Integer originPixel;
+ private String originColor;
+ private Integer originHorizon;
+ private Integer originVertical;
private Date registDatetime; //생성일시
private Date lastEditDatetime; //수정일시
private String returnMessage; //return message
-
+ private Integer cnt; //견적서 유무
}
diff --git a/src/main/java/com/interplug/qcast/biz/canvasStatus/CanvasStatusController.java b/src/main/java/com/interplug/qcast/biz/canvasStatus/CanvasStatusController.java
index eac5b83f..ff9d85c7 100644
--- a/src/main/java/com/interplug/qcast/biz/canvasStatus/CanvasStatusController.java
+++ b/src/main/java/com/interplug/qcast/biz/canvasStatus/CanvasStatusController.java
@@ -17,43 +17,44 @@ import org.springframework.web.bind.annotation.*;
public class CanvasStatusController {
private final CanvasStatusService canvasStatusService;
- @Operation(description = "계정에 해당하는 전체 견적서를 조회 한다.")
+ @Operation(description = "사용자(userId)에 해당하는 전체 캔버스를 조회 한다.")
@GetMapping("/canvas-statuses/{userId}")
- public List selectAllCanvasStatus(@PathVariable String userId) throws QcastException {
+ public List selectAllCanvasStatus(@PathVariable String userId)
+ throws QcastException {
return canvasStatusService.selectAllCanvasStatus(userId);
}
-
- @Operation(description = "견적서를 조회 한다.")
- @GetMapping("/canvas-statuses/by-object/{objectNo}/{userId}")
- public List selectObjectNoCanvasStatus(@PathVariable String objectNo, @PathVariable String userId) throws QcastException {
- return canvasStatusService.selectObjectNoCanvasStatus(objectNo, userId);
+
+ @Operation(description = "물건번호(objectNo)에 해당하는 캔버스를 조회 한다.")
+ @GetMapping("/canvas-statuses/by-object/{objectNo}")
+ public List selectObjectNoCanvasStatus(@PathVariable String objectNo)
+ throws QcastException {
+ return canvasStatusService.selectObjectNoCanvasStatus(objectNo);
}
-
- @Operation(description = "견적서를 등록 한다.")
+
+ @Operation(description = "캔버스를 등록 한다.")
@PostMapping("/canvas-statuses")
@ResponseStatus(HttpStatus.CREATED)
public Integer insertCanvasStatus(@RequestBody CanvasStatus cs) throws QcastException {
- return canvasStatusService.insertCanvasStatus(cs);
+ return canvasStatusService.insertCanvasStatus(cs);
}
- @Operation(description = "견적서를 수정 한다.")
+ @Operation(description = "캔버스를 수정 한다.")
@PutMapping("/canvas-statuses")
public void updateCanvasStatus(@RequestBody CanvasStatus cs) throws QcastException {
- canvasStatusService.updateCanvasStatus(cs);
+ canvasStatusService.updateCanvasStatus(cs);
}
-
- @Operation(description = "견적서를 삭제 한다.")
+
+ @Operation(description = "물건번호(objectNo)에 해당하는캔버스를 삭제 한다.")
@DeleteMapping("/canvas-statuses/by-object/{objectNo}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteObjectNoCanvasStatus(@PathVariable String objectNo) throws QcastException {
- canvasStatusService.deleteObjectNoCanvasStatus(objectNo);
+ canvasStatusService.deleteObjectNoCanvasStatus(objectNo);
}
-
- @Operation(description = "견적서의 이미지(템플릿)를 삭제 한다.")
+
+ @Operation(description = "id에 해당하는 캔버스를 삭제 한다.")
@DeleteMapping("/canvas-statuses/by-id/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteIdCanvasStatus(@PathVariable Integer id) throws QcastException {
- canvasStatusService.deleteIdCanvasStatus(id);
+ canvasStatusService.deleteIdCanvasStatus(id);
}
-
}
diff --git a/src/main/java/com/interplug/qcast/biz/canvasStatus/CanvasStatusMapper.java b/src/main/java/com/interplug/qcast/biz/canvasStatus/CanvasStatusMapper.java
index c76b892e..9b0691e3 100644
--- a/src/main/java/com/interplug/qcast/biz/canvasStatus/CanvasStatusMapper.java
+++ b/src/main/java/com/interplug/qcast/biz/canvasStatus/CanvasStatusMapper.java
@@ -1,47 +1,50 @@
package com.interplug.qcast.biz.canvasStatus;
import com.interplug.qcast.biz.canvasStatus.dto.CanvasStatus;
+import com.interplug.qcast.biz.canvasStatus.dto.CanvasStatusCopyRequest;
import com.interplug.qcast.biz.canvasStatus.dto.CanvasStatusResponse;
-
import java.util.List;
-
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface CanvasStatusMapper {
- // objectNo 생성(미사용)
- public CanvasStatus getCanvasStatusNewObjectNo(String userId);
-
- // imageName 생성(미사용)
- public CanvasStatus getCanvasStatusImageAdd(String objectNo);
-
- // 전체 견적서 조회
- public List selectAllCanvasStatus(String userId);
-
- // 견적서 조회(objectNo/userId)
- public List selectObjectNoCanvasStatus(String objectNo, String userId);
-
- // 견적서 조회(Max id)
- public List getMaxIdCanvasStatus(String objectNo, String userId);
-
- // 견적서 조회(id별)
- public List getIdCanvasStatus(Integer id);
-
- // 견적서 조회(objectNo)
- public List getObjectNoCanvasStatus(String objectNo);
-
- // 견적서 등록
- public void insertCanvasStatus(CanvasStatus cs);
-
- // 견적서 수정
- public void updateCanvasStatus(CanvasStatus cs);
-
- // 견적서 삭제
- public void deleteObjectNoCanvasStatus(String objectNo);
-
- // 이미지(템플릿) 삭제
- public void deleteIdCanvasStatus(Integer id);
+ // objectNo 생성(미사용)
+ public CanvasStatus getCanvasStatusNewObjectNo(String userId);
-
-}
\ No newline at end of file
+ // imageName 생성(미사용)
+ public CanvasStatus getCanvasStatusImageAdd(String objectNo);
+
+ // 전체 캔버스 조회 by 사용자(userId)
+ public List selectAllCanvasStatus(String userId);
+
+ // 캔버스 조회 by 물건번호(objectNo)
+ public List selectObjectNoCanvasStatus(String objectNo);
+
+ // 캔버스 조회 by Max(id)
+ public List getMaxIdCanvasStatus(String objectNo, String userId);
+
+ // 캔버스 조회 by id
+ public List getIdCanvasStatus(Integer id);
+
+ // 캔버스 조회 by 물건번호(objectNo)
+ public List getObjectNoCanvasStatus(String objectNo);
+
+ // 캔버스 등록
+ public void insertCanvasStatus(CanvasStatus cs);
+
+ // 캔버스 수정
+ public void updateCanvasStatus(CanvasStatus cs);
+
+ // 캔버스 삭제 by 물건번호(objectNo)
+ public void deleteObjectNoCanvasStatus(String objectNo);
+
+ // 캔버스 삭제 by id (미사용)
+ public void deleteIdCanvasStatus(Integer id);
+
+ // 캔버스 삭제플래그 변경 by id
+ public void updateDeletedCanvasStatus(Integer id);
+
+ // 캔버스 복사
+ public int copyCanvasStatus(CanvasStatusCopyRequest cs);
+}
diff --git a/src/main/java/com/interplug/qcast/biz/canvasStatus/CanvasStatusService.java b/src/main/java/com/interplug/qcast/biz/canvasStatus/CanvasStatusService.java
index 3f8cefc3..99e2ba64 100644
--- a/src/main/java/com/interplug/qcast/biz/canvasStatus/CanvasStatusService.java
+++ b/src/main/java/com/interplug/qcast/biz/canvasStatus/CanvasStatusService.java
@@ -1,122 +1,209 @@
package com.interplug.qcast.biz.canvasStatus;
+import com.interplug.qcast.biz.canvasBasicSetting.CanvasBasicSettingMapper;
+import com.interplug.qcast.biz.canvasBasicSetting.dto.CanvasBasicSettingInfo;
+import com.interplug.qcast.biz.canvasBasicSetting.dto.CanvasBasicSettingResponse;
+import com.interplug.qcast.biz.canvasBasicSetting.dto.RoofMaterialsAddInfo;
import com.interplug.qcast.biz.canvasStatus.dto.CanvasStatus;
+import com.interplug.qcast.biz.canvasStatus.dto.CanvasStatusCopyRequest;
import com.interplug.qcast.biz.canvasStatus.dto.CanvasStatusResponse;
import com.interplug.qcast.config.Exception.ErrorCode;
import com.interplug.qcast.config.Exception.QcastException;
-
-import lombok.RequiredArgsConstructor;
-
import java.util.List;
+import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
@Service
@RequiredArgsConstructor
public class CanvasStatusService {
- private final CanvasStatusMapper canvasStatusMapper;
+ private final CanvasStatusMapper canvasStatusMapper;
+ private final CanvasBasicSettingMapper canvasBasicSettingMapper;
- // 전체 견적서 조회
- public List selectAllCanvasStatus(String userId) throws QcastException {
- List result = null;
-
- if (userId != null && !userId.trim().isEmpty()) {
- result = canvasStatusMapper.selectAllCanvasStatus(userId);
- } else {
- throw new QcastException (ErrorCode.INVALID_INPUT_VALUE ,"올바르지 않은 입력값입니다.");
- }
-
- return result;
- }
+ // 사용자(userId)에 해당하는 전체 캔버스 조회
+ public List selectAllCanvasStatus(String userId) throws QcastException {
+ List result = null;
- // 견적서 조회(objectNo)
- public List selectObjectNoCanvasStatus(String objectNo, String userId) throws QcastException {
- List result = null;
-
- if (objectNo != null && !objectNo.trim().isEmpty() && userId != null && !userId.trim().isEmpty()) {
- result = canvasStatusMapper.selectObjectNoCanvasStatus(objectNo, userId);
- } else {
- throw new QcastException (ErrorCode.INVALID_INPUT_VALUE ,"올바르지 않은 입력값입니다.");
- }
-
- return result;
- }
+ try {
+ if (userId != null && !userId.trim().isEmpty()) {
+ result = canvasStatusMapper.selectAllCanvasStatus(userId);
+ } else {
+ throw new QcastException(ErrorCode.INVALID_INPUT_VALUE, "올바르지 않은 입력값입니다.");
+ }
+ } catch (Exception e) {
+ if (e instanceof QcastException) throw e;
+ throw new QcastException(ErrorCode.INTERNAL_SERVER_ERROR, e.getMessage());
+ }
- // 견적서 등록
- public Integer insertCanvasStatus(CanvasStatus cs) throws QcastException {
-
- Integer id = 0;
-
- // 데이터가 없으면 저장
- try {
- canvasStatusMapper.insertCanvasStatus(cs);
-
- // 데이터 저장 후 Max id 확인
- List maxId = canvasStatusMapper.getMaxIdCanvasStatus(cs.getObjectNo(), cs.getUserId());
- id = maxId.get(0).getId();
-
- } catch (Exception e) {
- throw new QcastException (ErrorCode.INVALID_INPUT_VALUE ,"견적서 등록 중 오류 발생");
- }
-
- // 생성된 id 반환
- return id;
- }
+ return result;
+ }
- // 견적서 수정
- public void updateCanvasStatus(CanvasStatus cs) throws QcastException {
+ // 물건번호(objectNo)에 해당하는 캔버스 조회
+ public List selectObjectNoCanvasStatus(String objectNo)
+ throws QcastException {
+ List result = null;
- if (cs.getId() == null) {
- throw new QcastException (ErrorCode.INVALID_INPUT_VALUE ,"올바르지 않은 입력값입니다.");
- }
-
- // 먼저 데이터가 존재하는지 확인
- List existingStatus = canvasStatusMapper.getIdCanvasStatus(cs.getId());
+ try {
+ if (objectNo != null && !objectNo.trim().isEmpty()) {
+ result = canvasStatusMapper.selectObjectNoCanvasStatus(objectNo);
+ } else {
+ throw new QcastException(ErrorCode.INVALID_INPUT_VALUE, "올바르지 않은 입력값입니다.");
+ }
+ } catch (Exception e) {
+ if (e instanceof QcastException) throw e;
+ throw new QcastException(ErrorCode.INTERNAL_SERVER_ERROR, e.getMessage());
+ }
- // 데이터가 존재하지 않으면 수정하지 않고 예외를 던짐
- if (existingStatus.size() > 0) {
- canvasStatusMapper.updateCanvasStatus(cs);
- } else {
- throw new QcastException (ErrorCode.NOT_FOUND ,"수정할 견적서가 존재하지 않습니다.");
- }
-
- }
+ return result;
+ }
- // 전체 견적서 삭제
- public void deleteObjectNoCanvasStatus(String objectNo) throws QcastException {
-
- if (objectNo == null || objectNo.trim().isEmpty()) {
- throw new QcastException (ErrorCode.INVALID_INPUT_VALUE ,"올바르지 않은 입력값입니다.");
- }
-
- // 먼저 데이터가 존재하는지 확인
- List existingStatus = canvasStatusMapper.getObjectNoCanvasStatus(objectNo);
-
- // 데이터가 존재하지 않으면 수정하지 않고 예외를 던짐
- if (existingStatus.size() > 0) {
- canvasStatusMapper.deleteObjectNoCanvasStatus(objectNo);
- } else {
- throw new QcastException (ErrorCode.NOT_FOUND ,"삭제할 견적서가 존재하지 않습니다.");
- }
-
- }
+ // 캔버스 등록
+ public Integer insertCanvasStatus(CanvasStatus cs) throws QcastException {
- // 이미지(템플릿) 삭제
- public void deleteIdCanvasStatus(Integer id) throws QcastException {
-
- if (id == null) {
- throw new QcastException (ErrorCode.INVALID_INPUT_VALUE ,"올바르지 않은 입력값입니다.");
- }
-
- // 먼저 데이터가 존재하는지 확인
- List existingStatus = canvasStatusMapper.getIdCanvasStatus(id);
-
- // 데이터가 존재하지 않으면 수정하지 않고 예외를 던짐
- if (existingStatus.size() > 0) {
- canvasStatusMapper.deleteIdCanvasStatus(id);
- } else {
- throw new QcastException (ErrorCode.NOT_FOUND ,"삭제할 견적서가 존재하지 않습니다.");
- }
-
- }
+ Integer id = 0;
+ // 데이터가 없으면 저장
+ try {
+ canvasStatusMapper.insertCanvasStatus(cs);
+
+ // 데이터 저장 후 Max id 확인
+ List maxId =
+ canvasStatusMapper.getMaxIdCanvasStatus(cs.getObjectNo(), cs.getUserId());
+ id = maxId.get(0).getId();
+
+ } catch (Exception e) {
+ throw new QcastException(ErrorCode.INTERNAL_SERVER_ERROR, e.getMessage());
+ }
+
+ // 생성된 id 반환
+ return id;
+ }
+
+ // 캔버스 수정
+ public void updateCanvasStatus(CanvasStatus cs) throws QcastException {
+
+ if (cs.getId() == null) {
+ throw new QcastException(ErrorCode.INVALID_INPUT_VALUE, "올바르지 않은 입력값입니다.");
+ }
+
+ try {
+ // 먼저 데이터가 존재하는지 확인
+ List existingStatus = canvasStatusMapper.getIdCanvasStatus(cs.getId());
+
+ // 데이터가 존재하지 않으면 수정하지 않고 예외를 던짐
+ if (existingStatus.size() > 0) {
+ canvasStatusMapper.updateCanvasStatus(cs);
+ } else {
+ throw new QcastException(ErrorCode.NOT_FOUND, "수정할 캔버스가 존재하지 않습니다.");
+ }
+ } catch (Exception e) {
+ if (e instanceof QcastException) throw e;
+ throw new QcastException(ErrorCode.INTERNAL_SERVER_ERROR, e.getMessage());
+ }
+ }
+
+ // 물건번호(objectNo)에 해당하는 캔버스 삭제
+ public void deleteObjectNoCanvasStatus(String objectNo) throws QcastException {
+
+ if (objectNo == null || objectNo.trim().isEmpty()) {
+ throw new QcastException(ErrorCode.INVALID_INPUT_VALUE, "올바르지 않은 입력값입니다.");
+ }
+
+ try {
+ // 먼저 데이터가 존재하는지 확인
+ List existingStatus =
+ canvasStatusMapper.getObjectNoCanvasStatus(objectNo);
+
+ // 데이터가 존재하지 않으면 수정하지 않고 예외를 던짐
+ if (existingStatus.size() > 0) {
+ canvasStatusMapper.deleteObjectNoCanvasStatus(objectNo);
+ } else {
+ throw new QcastException(ErrorCode.NOT_FOUND, "삭제할 캔버스가 존재하지 않습니다.");
+ }
+ } catch (Exception e) {
+ if (e instanceof QcastException) throw e;
+ throw new QcastException(ErrorCode.INTERNAL_SERVER_ERROR, e.getMessage());
+ }
+ }
+
+ // id에 해당하는 캔버스 삭제
+ public void deleteIdCanvasStatus(Integer id) throws QcastException {
+
+ if (id == null) {
+ throw new QcastException(ErrorCode.INVALID_INPUT_VALUE, "올바르지 않은 입력값입니다.");
+ }
+
+ try {
+ // 먼저 데이터가 존재하는지 확인
+ List existingStatus = canvasStatusMapper.getIdCanvasStatus(id);
+
+ // 데이터가 존재하지 않으면 처리하지 않고 예외를 던짐
+ if (existingStatus.size() > 0) {
+ // 데이터를 삭제하는 기존 방식에서 데이터를 삭제하지 않고 deleted 데이터를 바꾸는 방식으로 변경 (2025.02.11)
+ // canvasStatusMapper.deleteIdCanvasStatus(id);
+ canvasStatusMapper.updateDeletedCanvasStatus(id);
+ } else {
+ throw new QcastException(ErrorCode.NOT_FOUND, "삭제할 캔버스가 존재하지 않습니다.");
+ }
+ } catch (Exception e) {
+ if (e instanceof QcastException) throw e;
+ throw new QcastException(ErrorCode.INTERNAL_SERVER_ERROR, e.getMessage());
+ }
+ }
+
+ // 캔버스 복사 후 등록
+ @Transactional
+ public int copyCanvasStatus(CanvasStatusCopyRequest cs, Boolean flag) throws QcastException {
+ try {
+
+ if (flag) {
+ // 배치면 초기설정 정보 조회
+ List selData =
+ canvasBasicSettingMapper.selectCanvasBasicSetting(
+ cs.getOriginObjectNo(), Integer.parseInt(cs.getOriginPlanNo()));
+
+ if (!selData.isEmpty()) {
+ // 첫 번째 데이터만 CanvasBasicSettingInfo로 변환하여 insert 실행
+ CanvasBasicSettingResponse firstData = selData.get(0);
+
+ // CanvasBasicSettingResponse → CanvasBasicSettingInfo 변환
+ CanvasBasicSettingInfo basicSettingInfo = new CanvasBasicSettingInfo();
+ basicSettingInfo.setObjectNo(cs.getObjectNo());
+ basicSettingInfo.setPlanNo(Integer.parseInt(cs.getPlanNo()));
+ basicSettingInfo.setRoofSizeSet(firstData.getRoofSizeSet());
+ basicSettingInfo.setRoofAngleSet(firstData.getRoofAngleSet());
+
+ // 도면/치수/각도 정보 insert (한 번만 실행)
+ canvasBasicSettingMapper.insertCanvasBasicSetting(basicSettingInfo);
+
+ // 나머지 RoofMaterialsAddInfo 처리
+ for (CanvasBasicSettingResponse data : selData) {
+ // CanvasBasicSettingResponse → RoofMaterialsAddInfo 변환
+ RoofMaterialsAddInfo roofMaterials = new RoofMaterialsAddInfo();
+ roofMaterials.setObjectNo(cs.getObjectNo());
+ roofMaterials.setPlanNo(Integer.parseInt(cs.getPlanNo()));
+ roofMaterials.setRoofApply(data.isRoofApply());
+ roofMaterials.setRoofSeq(data.getRoofSeq());
+ roofMaterials.setRoofMatlCd(data.getRoofMatlCd());
+ roofMaterials.setRoofWidth(data.getRoofWidth());
+ roofMaterials.setRoofHeight(data.getRoofHeight());
+ roofMaterials.setRoofHajebichi(data.getRoofHajebichi());
+ roofMaterials.setRoofGap(data.getRoofGap());
+ roofMaterials.setRoofLayout(data.getRoofLayout());
+ roofMaterials.setRoofPitch(data.getRoofPitch());
+ roofMaterials.setRoofAngle(data.getRoofAngle());
+
+ // 지붕재 추가 Setting insert (여러 개 가능)
+ canvasBasicSettingMapper.insertRoofMaterialsAdd(roofMaterials);
+ }
+ }
+ }
+
+ canvasStatusMapper.copyCanvasStatus(cs);
+
+ return cs.getId();
+ } catch (Exception e) {
+ throw new QcastException(ErrorCode.INTERNAL_SERVER_ERROR, "캔버스 복사 중 오류 발생");
+ }
+ }
}
diff --git a/src/main/java/com/interplug/qcast/biz/canvasStatus/dto/CanvasStatus.java b/src/main/java/com/interplug/qcast/biz/canvasStatus/dto/CanvasStatus.java
index f0a74443..f5e25418 100644
--- a/src/main/java/com/interplug/qcast/biz/canvasStatus/dto/CanvasStatus.java
+++ b/src/main/java/com/interplug/qcast/biz/canvasStatus/dto/CanvasStatus.java
@@ -7,7 +7,7 @@ public class CanvasStatus {
private Integer id; // PK ID
private String userId; // 사용자 ID
private String objectNo; // 견적서 번호
- private String imageName; // 이미지명
+ private String planNo; // 플랜 번호
private String canvasStatus; // 캠버스 상태
private String bgImageName; // 배경 이미지명
private String mapPositionAddress; // 배경 CAD 파일명
diff --git a/src/main/java/com/interplug/qcast/biz/canvasStatus/dto/CanvasStatusCopyRequest.java b/src/main/java/com/interplug/qcast/biz/canvasStatus/dto/CanvasStatusCopyRequest.java
new file mode 100644
index 00000000..55437dcd
--- /dev/null
+++ b/src/main/java/com/interplug/qcast/biz/canvasStatus/dto/CanvasStatusCopyRequest.java
@@ -0,0 +1,28 @@
+package com.interplug.qcast.biz.canvasStatus.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@Schema(description = "캔버스 복사 요청 객체")
+public class CanvasStatusCopyRequest {
+ @Schema(description = "ID", hidden = true)
+ private Integer id;
+
+ @Schema(description = "원본 물건 번호")
+ private String originObjectNo;
+
+ @Schema(description = "원본 플랜 번호")
+ private String originPlanNo;
+
+ @Schema(description = "복사본 저장할 사용자 ID")
+ private String userId;
+
+ @Schema(description = "복사본 저장할 물건 번호")
+ private String objectNo;
+
+ @Schema(description = "복사본 저장할 플랜 번호")
+ private String planNo;
+}
diff --git a/src/main/java/com/interplug/qcast/biz/canvasStatus/dto/CanvasStatusResponse.java b/src/main/java/com/interplug/qcast/biz/canvasStatus/dto/CanvasStatusResponse.java
index e6530eda..4fb4a44c 100644
--- a/src/main/java/com/interplug/qcast/biz/canvasStatus/dto/CanvasStatusResponse.java
+++ b/src/main/java/com/interplug/qcast/biz/canvasStatus/dto/CanvasStatusResponse.java
@@ -10,7 +10,7 @@ public class CanvasStatusResponse {
private Integer id; // PK ID
private String userId; // 사용자 ID
private String objectNo; // 견적서 번호
- private String imageName; // 이미지명
+ private String planNo; // 플랜 번호
private String canvasStatus; // 캠버스 상태
private Date registDatetime; // 생성일시
private Date lastEditDatetime; // 수정일시
diff --git a/src/main/java/com/interplug/qcast/biz/canvaspopupstatus/CanvasPopupStatusController.java b/src/main/java/com/interplug/qcast/biz/canvaspopupstatus/CanvasPopupStatusController.java
new file mode 100644
index 00000000..086bd070
--- /dev/null
+++ b/src/main/java/com/interplug/qcast/biz/canvaspopupstatus/CanvasPopupStatusController.java
@@ -0,0 +1,79 @@
+package com.interplug.qcast.biz.canvaspopupstatus;
+
+import com.interplug.qcast.biz.canvaspopupstatus.dto.CanvasPopupStatus;
+import com.interplug.qcast.config.Exception.ErrorCode;
+import com.interplug.qcast.config.Exception.QcastException;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/api/v1/canvas-popup-status")
+@RequiredArgsConstructor
+@Tag(name = "CanvasPopupStatusController", description = "Canvas Popup Status 관련 API")
+public class CanvasPopupStatusController {
+
+ private final CanvasPopupStatusService canvasPopupStatusService;
+
+ /**
+ * 캔버스 팝업 상태를 조회한다.
+ *
+ * @param objectNo 물건정보 번호
+ * @param planNo plan 번호
+ * @param popupType 캔버스 팝업 단계
+ * @return 조회된 CanvasPopupStatus 객체, 조회된 데이터가 없을 경우 빈 객체 반환
+ */
+ @Operation(description = "캔버스 팝업 상태를 조회한다.")
+ @GetMapping
+ public CanvasPopupStatus getCanvasPopupStatus(
+ @RequestParam @Schema(description = "물건정보 번호") String objectNo,
+ @RequestParam @Schema(description = "plan 번호") Integer planNo,
+ @RequestParam @Schema(description = "캔버스 팝업 단계") String popupType)
+ throws QcastException {
+ if (objectNo == null
+ || objectNo.trim().isEmpty()
+ || planNo == null
+ || popupType == null
+ || popupType.trim().isEmpty()) {
+ throw new QcastException(ErrorCode.INVALID_INPUT_VALUE);
+ }
+ return canvasPopupStatusService.selectCanvasPopupStatus(objectNo, planNo, popupType);
+ }
+
+ /**
+ * 캔버스 팝업 상태를 저장 또는 수정한다.
+ *
+ * @param canvasPopupStatus 저장 또는 수정할 CanvasPopupStatus 객체
+ * @throws QcastException 저장 또는 수정 중 예외 발생 시
+ */
+ @Operation(description = "캔버스 팝업 상태를 저장 또는 수정한다.")
+ @PostMapping
+ public void saveCanvasPopupStatus(@RequestBody CanvasPopupStatus canvasPopupStatus)
+ throws QcastException {
+ canvasPopupStatusService.saveCanvasPopupStatus(canvasPopupStatus);
+ }
+
+ /**
+ * 캔버스 팝업 상태를 삭제한다. (미사용)
+ *
+ * @param canvasPopupStatus 삭제할 CanvasPopupStatus 객체
+ * @throws QcastException 삭제 중 예외 발생 시
+ */
+ @Operation(description = "캔버스 팝업 상태를 삭제한다. (미사용)")
+ @DeleteMapping
+ @ResponseStatus(HttpStatus.NO_CONTENT)
+ public void deleteCanvasPopupStatus(@RequestBody CanvasPopupStatus canvasPopupStatus)
+ throws QcastException {
+ canvasPopupStatusService.deleteCanvasPopupStatus(canvasPopupStatus);
+ }
+}
diff --git a/src/main/java/com/interplug/qcast/biz/canvaspopupstatus/CanvasPopupStatusMapper.java b/src/main/java/com/interplug/qcast/biz/canvaspopupstatus/CanvasPopupStatusMapper.java
new file mode 100644
index 00000000..bfe59520
--- /dev/null
+++ b/src/main/java/com/interplug/qcast/biz/canvaspopupstatus/CanvasPopupStatusMapper.java
@@ -0,0 +1,37 @@
+package com.interplug.qcast.biz.canvaspopupstatus;
+
+import com.interplug.qcast.biz.canvaspopupstatus.dto.CanvasPopupStatus;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface CanvasPopupStatusMapper {
+
+ /**
+ * Canvas Popup Status 조회
+ *
+ * @param canvasPopupStatus 조회할 CanvasPopupStatus 객체
+ * @return 조회된 CanvasPopupStatus 객체
+ */
+ public CanvasPopupStatus selectCanvasPopupStatus(CanvasPopupStatus canvasPopupStatus);
+
+ /**
+ * Canvas Popup Status 생성
+ *
+ * @param canvasPopupStatus 생성할 CanvasPopupStatus 객체
+ */
+ public void insertCanvasPopupStatus(CanvasPopupStatus canvasPopupStatus);
+
+ /**
+ * Canvas Popup Status 수정
+ *
+ * @param canvasPopupStatus 수정할 CanvasPopupStatus 객체
+ */
+ public void updateCanvasPopupStatus(CanvasPopupStatus canvasPopupStatus);
+
+ /**
+ * Canvas Popup Status 삭제
+ *
+ * @param canvasPopupStatus 삭제할 CanvasPopupStatus 객체
+ */
+ public void deleteCanvasPopupStatus(CanvasPopupStatus canvasPopupStatus);
+}
diff --git a/src/main/java/com/interplug/qcast/biz/canvaspopupstatus/CanvasPopupStatusService.java b/src/main/java/com/interplug/qcast/biz/canvaspopupstatus/CanvasPopupStatusService.java
new file mode 100644
index 00000000..480832bc
--- /dev/null
+++ b/src/main/java/com/interplug/qcast/biz/canvaspopupstatus/CanvasPopupStatusService.java
@@ -0,0 +1,149 @@
+package com.interplug.qcast.biz.canvaspopupstatus;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.interplug.qcast.biz.canvasBasicSetting.CanvasBasicSettingMapper;
+import com.interplug.qcast.biz.canvasBasicSetting.dto.CanvasBasicSettingResponse;
+import com.interplug.qcast.biz.canvaspopupstatus.dto.CanvasPopupStatus;
+import com.interplug.qcast.biz.estimate.dto.EstimateApiResponse;
+import com.interplug.qcast.config.Exception.ErrorCode;
+import com.interplug.qcast.config.Exception.QcastException;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Service
+@RequiredArgsConstructor
+@Transactional(readOnly = true)
+public class CanvasPopupStatusService {
+
+ private final CanvasPopupStatusMapper canvasPopupStatusMapper;
+ private final CanvasBasicSettingMapper canvasBasicSettingMapper;
+
+ /**
+ * Canvas Popup Status 조회
+ *
+ * @param objectNo 조회할 object 번호
+ * @param planNo 조회할 plan 번호
+ * @param popupType 조회할 popup 타입
+ * @return 조회된 CanvasPopupStatus 객체, 조회된 데이터가 없을 경우 빈 객체 반환
+ */
+ public CanvasPopupStatus selectCanvasPopupStatus(
+ String objectNo, Integer planNo, String popupType) throws QcastException {
+ CanvasPopupStatus request =
+ CanvasPopupStatus.builder().objectNo(objectNo).planNo(planNo).popupType(popupType).build();
+ CanvasPopupStatus cps = canvasPopupStatusMapper.selectCanvasPopupStatus(request);
+ return cps != null ? cps : CanvasPopupStatus.builder().build();
+ }
+
+ /**
+ * Canvas Popup Status 저장 - 이미 존재하는 데이터인 경우 수정, 없는 경우 생성
+ *
+ * @param cps 저장할 CanvasPopupStatus 객체
+ * @throws QcastException 저장 중 예외 발생 시
+ */
+ @Transactional
+ public void saveCanvasPopupStatus(CanvasPopupStatus cps) throws QcastException {
+ CanvasPopupStatus chk = canvasPopupStatusMapper.selectCanvasPopupStatus(cps);
+ if (chk == null) {
+ createCanvasPopupStatus(cps);
+ } else {
+ updateCanvasPopupStatus(cps);
+ }
+ //기초정보 업데이트 (roofHajebichi)
+
+ String hajebichi = cps.getHajebichi();
+ if(hajebichi != null && !hajebichi.isEmpty()){
+ CanvasBasicSettingResponse canvasBasicSettingResponse = new CanvasBasicSettingResponse();
+ canvasBasicSettingResponse.setObjectNo(cps.getObjectNo());
+ canvasBasicSettingResponse.setPlanNo(cps.getPlanNo());
+ canvasBasicSettingResponse.setRoofHajebichi(Integer.valueOf(hajebichi));
+ canvasBasicSettingMapper.updateHajebichRoofMaterialsAdd(canvasBasicSettingResponse);
+ }
+
+
+// String popupStatus = cps.getPopupStatus();
+// List hajebichiList = new ArrayList<>();//getHajebichiValues(popupStatus, 0, 1); // roofIndex=0, planNo=1인 hajebichi 값들 조회
+//
+// try {
+// ObjectMapper mapper = new ObjectMapper();
+// JsonNode rootNode = mapper.readTree(popupStatus);
+// JsonNode roofConstructions = rootNode.get("roofConstructions");
+// if (roofConstructions.isArray()) {
+// for (JsonNode construction : roofConstructions) {
+// JsonNode addRoof = construction.get("addRoof");
+// if (addRoof != null
+// && addRoof.get("roofIndex").asInt() == 0
+// && addRoof.get("planNo").asInt() == cps.getPlanNo()) {
+// hajebichiList.add(addRoof.get("hajebichi").asInt());
+// }
+// }
+// }
+//
+// } catch (JsonProcessingException e) {
+// throw new QcastException(ErrorCode.INTERNAL_SERVER_ERROR, "JSON 파싱 오류");
+// }
+// if(!hajebichiList.isEmpty()){
+// CanvasBasicSettingResponse canvasBasicSettingResponse = new CanvasBasicSettingResponse();
+// canvasBasicSettingResponse.setObjectNo(cps.getObjectNo());
+// canvasBasicSettingResponse.setPlanNo(cps.getPlanNo());
+// canvasBasicSettingResponse.setRoofHajebichi(hajebichiList.get(0));
+//
+
+// if(canvasBasicSettingResponse.getRoofHajebichi() > 0){
+// canvasBasicSettingMapper.updateHajebichRoofMaterialsAdd(canvasBasicSettingResponse);
+// }
+//
+// }
+
+
+ }
+
+ /**
+ * Canvas Popup Status 생성
+ *
+ * @param cps 생성할 CanvasPopupStatus 객체
+ * @throws QcastException 생성 중 예외 발생 시
+ */
+ @Transactional
+ public void createCanvasPopupStatus(CanvasPopupStatus cps) throws QcastException {
+ try {
+ canvasPopupStatusMapper.insertCanvasPopupStatus(cps);
+ } catch (Exception e) {
+ throw new QcastException(ErrorCode.INTERNAL_SERVER_ERROR, e.getMessage());
+ }
+ }
+
+ /**
+ * Canvas Popup Status 수정
+ *
+ * @param cps 수정할 CanvasPopupStatus 객체
+ * @throws QcastException 수정 중 예외 발생 시
+ */
+ public void updateCanvasPopupStatus(CanvasPopupStatus cps) throws QcastException {
+ try {
+ canvasPopupStatusMapper.updateCanvasPopupStatus(cps);
+ } catch (Exception e) {
+ throw new QcastException(ErrorCode.INTERNAL_SERVER_ERROR, e.getMessage());
+ }
+ }
+
+ /**
+ * Canvas Popup Status 삭제
+ *
+ * @param cps 삭제할 CanvasPopupStatus 객체
+ * @throws QcastException 삭제 중 예외 발생 시
+ */
+ public void deleteCanvasPopupStatus(CanvasPopupStatus cps) throws QcastException {
+ try {
+ canvasPopupStatusMapper.deleteCanvasPopupStatus(cps);
+ } catch (Exception e) {
+ throw new QcastException(ErrorCode.INTERNAL_SERVER_ERROR, e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/com/interplug/qcast/biz/canvaspopupstatus/dto/CanvasPopupStatus.java b/src/main/java/com/interplug/qcast/biz/canvaspopupstatus/dto/CanvasPopupStatus.java
new file mode 100644
index 00000000..788bc89b
--- /dev/null
+++ b/src/main/java/com/interplug/qcast/biz/canvaspopupstatus/dto/CanvasPopupStatus.java
@@ -0,0 +1,32 @@
+package com.interplug.qcast.biz.canvaspopupstatus.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@Builder
+@Schema(description = "캔버스 팝업 상태")
+public class CanvasPopupStatus {
+
+ // @Schema(description = "id")
+ // private Long id;
+
+ @Schema(description = "물건정보 번호")
+ private String objectNo;
+
+ @Schema(description = "plan 번호")
+ private Integer planNo;
+
+ @Schema(description = "캔버스 팝업 단계")
+ private String popupType;
+
+ @Schema(description = "캔버스 팝업 상태 데이터")
+ private String popupStatus;
+
+ @Schema(description = "하제비치")
+ private String hajebichi;
+
+}
diff --git a/src/main/java/com/interplug/qcast/biz/commCode/CommCodeMapper.java b/src/main/java/com/interplug/qcast/biz/commCode/CommCodeMapper.java
index 7ef35426..123fffac 100644
--- a/src/main/java/com/interplug/qcast/biz/commCode/CommCodeMapper.java
+++ b/src/main/java/com/interplug/qcast/biz/commCode/CommCodeMapper.java
@@ -3,14 +3,54 @@ package com.interplug.qcast.biz.commCode;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.interplug.qcast.biz.commCode.dto.CodeRes;
+import com.interplug.qcast.biz.commCode.dto.DetailCodeRequest;
+import com.interplug.qcast.biz.commCode.dto.HeadCodeRequest;
@Mapper
public interface CommCodeMapper {
+ /**
+ * Qcast 공통코드 저장시 실시간 동기화
+ *
+ * @param codeRes
+ * @return
+ */
int setCommHUpdate(CodeRes codeRes);
+ /**
+ * Qcast 공통코드 저장시 실시간 동기화
+ *
+ * @param codeRes
+ * @return
+ */
int setCommLUpdate(CodeRes codeRes);
+ /**
+ * QCast에서 사용하는 공통코드 조회
+ *
+ * @return
+ */
List selectQcastCommCode();
+
+ /**
+ * 배치 공통코드 헤더 정보 동기화 저장
+ *
+ * @param headCodeReq
+ * @return
+ * @throws Exception
+ */
+ int setHeadCodeSyncSave(HeadCodeRequest headCodeReq) throws Exception;
+
+ /**
+ * 배치 공통코드 상세 정보 동기화 저장
+ *
+ * @param detailCodeReq
+ * @return
+ * @throws Exception
+ */
+ int setCommonCodeSyncSave(DetailCodeRequest detailCodeReq) throws Exception;
+
+
+
}
diff --git a/src/main/java/com/interplug/qcast/biz/commCode/CommCodeService.java b/src/main/java/com/interplug/qcast/biz/commCode/CommCodeService.java
index 739dfb73..4385e1a2 100644
--- a/src/main/java/com/interplug/qcast/biz/commCode/CommCodeService.java
+++ b/src/main/java/com/interplug/qcast/biz/commCode/CommCodeService.java
@@ -1,13 +1,17 @@
package com.interplug.qcast.biz.commCode;
+import java.util.ArrayList;
+import java.util.List;
+import org.springframework.stereotype.Service;
import com.interplug.qcast.biz.commCode.dto.CodeReq;
import com.interplug.qcast.biz.commCode.dto.CodeRes;
import com.interplug.qcast.biz.commCode.dto.CommCodeRes;
-import java.util.ArrayList;
-import java.util.List;
+import com.interplug.qcast.biz.commCode.dto.DetailCodeRequest;
+import com.interplug.qcast.biz.commCode.dto.HeadCodeRequest;
import lombok.RequiredArgsConstructor;
-import org.springframework.stereotype.Service;
+import lombok.extern.slf4j.Slf4j;
+@Slf4j
@Service
@RequiredArgsConstructor
public class CommCodeService {
@@ -49,17 +53,55 @@ public class CommCodeService {
public List selectQcastCommCode() {
List result = commCodeMapper.selectQcastCommCode();
List commCodeList = new ArrayList<>();
- result.forEach(
- cr -> {
- commCodeList.add(
- CommCodeRes.builder()
- .clHeadCd(cr.getClHeadCd())
- .clCode(cr.getClCode())
- .clCodeNm(cr.getClCodeNm())
- .clCodeJp(cr.getClCodeJp())
- .clPriority(cr.getClPriority())
- .build());
- });
+ result.forEach(cr -> {
+ commCodeList.add(CommCodeRes.builder().clHeadCd(cr.getClHeadCd()).clCode(cr.getClCode())
+ .clCodeNm(cr.getClCodeNm()).clCodeJp(cr.getClCodeJp()).clPriority(cr.getClPriority())
+ .clRefChr1(cr.getClRefChr1()).clRefChr2(cr.getClRefChr2()).build());
+ });
return commCodeList;
}
+
+ /**
+ * 헤더코드 동기화
+ *
+ * @param headCodeList
+ * @throws Exception
+ */
+ public void setHeaderCodeSyncSave(List headCodeList) throws Exception {
+ // 헤더코드 동기화
+ for (HeadCodeRequest headCodeReq : headCodeList) {
+ try {
+ if ("Y".equals(headCodeReq.getQcCommYn()) && "N".equals(headCodeReq.getDelYn())) {
+ headCodeReq.setDelFlg(0);
+ } else {
+ headCodeReq.setDelFlg(1);
+ }
+ commCodeMapper.setHeadCodeSyncSave(headCodeReq);
+ } catch (Exception e) {
+ log.error(e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * 상세코드 동기화
+ *
+ * @param detailCodeList
+ * @throws Exception
+ */
+ public void setCommonCodeSyncSave(List detailCodeList) throws Exception {
+ // 상세코드 동기화
+ for (DetailCodeRequest detailCodeReq : detailCodeList) {
+ try {
+ if ("A".equals(detailCodeReq.getStatCd()) && "N".equals(detailCodeReq.getDelYn())) {
+ detailCodeReq.setDelFlg(0);
+ } else {
+ detailCodeReq.setDelFlg(1);
+ }
+ commCodeMapper.setCommonCodeSyncSave(detailCodeReq);
+ } catch (Exception e) {
+ log.error(e.getMessage());
+ }
+ }
+ }
}
diff --git a/src/main/java/com/interplug/qcast/biz/commCode/dto/CommCodeRes.java b/src/main/java/com/interplug/qcast/biz/commCode/dto/CommCodeRes.java
index 541404bb..8fe9bc5f 100644
--- a/src/main/java/com/interplug/qcast/biz/commCode/dto/CommCodeRes.java
+++ b/src/main/java/com/interplug/qcast/biz/commCode/dto/CommCodeRes.java
@@ -11,4 +11,6 @@ public class CommCodeRes {
private String clCodeNm;
private String clCodeJp;
private Integer clPriority;
+ private String clRefChr1;
+ private String clRefChr2;
}
diff --git a/src/main/java/com/interplug/qcast/biz/commCode/dto/CommonCodeSyncResponse.java b/src/main/java/com/interplug/qcast/biz/commCode/dto/CommonCodeSyncResponse.java
new file mode 100644
index 00000000..b2134d7e
--- /dev/null
+++ b/src/main/java/com/interplug/qcast/biz/commCode/dto/CommonCodeSyncResponse.java
@@ -0,0 +1,17 @@
+package com.interplug.qcast.biz.commCode.dto;
+
+import java.util.List;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class CommonCodeSyncResponse {
+
+ @Schema(description = "공통코드 H 목록")
+ private List apiHeadCdList;
+
+ @Schema(description = "공통코드 L 목록")
+ private List apiCommCdList;
+}
diff --git a/src/main/java/com/interplug/qcast/biz/commCode/dto/DetailCodeRequest.java b/src/main/java/com/interplug/qcast/biz/commCode/dto/DetailCodeRequest.java
new file mode 100644
index 00000000..5d1377ca
--- /dev/null
+++ b/src/main/java/com/interplug/qcast/biz/commCode/dto/DetailCodeRequest.java
@@ -0,0 +1,28 @@
+package com.interplug.qcast.biz.commCode.dto;
+
+import lombok.Data;
+
+@Data
+public class DetailCodeRequest {
+ private String headCd;
+ private String code;
+ private String readCd;
+ private String codeNm;
+ private String codeJp;
+ private String code4Th;
+ private String refChr1;
+ private String refChr2;
+ private String refChr3;
+ private String refChr4;
+ private String refChr5;
+ private Integer refNum1;
+ private Integer refNum2;
+ private Integer refNum3;
+ private Integer refNum4;
+ private Integer refNum5;
+ private Integer priority;
+ private String refCnt;
+ private String statCd;
+ private Integer delFlg;
+ private String delYn;
+}
diff --git a/src/main/java/com/interplug/qcast/biz/commCode/dto/HeadCodeRequest.java b/src/main/java/com/interplug/qcast/biz/commCode/dto/HeadCodeRequest.java
new file mode 100644
index 00000000..7ed11c3f
--- /dev/null
+++ b/src/main/java/com/interplug/qcast/biz/commCode/dto/HeadCodeRequest.java
@@ -0,0 +1,26 @@
+package com.interplug.qcast.biz.commCode.dto;
+
+import lombok.Data;
+
+@Data
+public class HeadCodeRequest {
+ private String headCd;
+ private String headId;
+ private String headNm;
+ private String headJp;
+ private String head4Th;
+ private String refChr1;
+ private String refChr2;
+ private String refChr3;
+ private String refChr4;
+ private String refChr5;
+ private String refNum1;
+ private String refNum2;
+ private String refNum3;
+ private String refNum4;
+ private String refNum5;
+ private String remarks;
+ private Integer delFlg;
+ private String qcCommYn;
+ private String delYn;
+}
diff --git a/src/main/java/com/interplug/qcast/biz/community/BoardController.java b/src/main/java/com/interplug/qcast/biz/community/BoardController.java
index c2b980e2..3e140143 100644
--- a/src/main/java/com/interplug/qcast/biz/community/BoardController.java
+++ b/src/main/java/com/interplug/qcast/biz/community/BoardController.java
@@ -1,19 +1,16 @@
package com.interplug.qcast.biz.community;
-import org.springframework.http.HttpStatus;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.ModelAttribute;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.ResponseStatus;
-import org.springframework.web.bind.annotation.RestController;
import com.interplug.qcast.biz.community.dto.BoardRequest;
import com.interplug.qcast.biz.community.dto.BoardResponse;
+import com.interplug.qcast.biz.file.dto.FileRequest;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.*;
@Slf4j
@RestController
@@ -38,12 +35,21 @@ public class BoardController {
return boardService.getBoardDetail(boardRequest);
}
+ @Operation(description = "문의를 저장한다.")
+ @PostMapping("/saveQna")
+ @ResponseStatus(HttpStatus.OK)
+ public BoardResponse getBoardQnaSave(BoardRequest boardRequest, HttpServletRequest request) throws Exception {
+ return boardService.getBoardQnaSave(boardRequest, request);
+ }
+
@Operation(description = "커뮤니티(공지사항, FAQ, 자료다운로드) 파일 다운로드를 진행한다.")
@GetMapping("/file/download")
@ResponseStatus(HttpStatus.OK)
- public void getFileDownload(HttpServletResponse response,
- @RequestParam(required = true) String encodeFileNo) throws Exception {
- boardService.getFileDownload(response, encodeFileNo);
+ public void getFileDownload(
+ HttpServletResponse response,
+ @RequestParam(required = true) String keyNo,
+ @RequestParam String zipYn)
+ throws Exception {
+ boardService.getFileDownload(response, keyNo, zipYn);
}
-
}
diff --git a/src/main/java/com/interplug/qcast/biz/community/BoardService.java b/src/main/java/com/interplug/qcast/biz/community/BoardService.java
index e7451fe2..0b73887b 100644
--- a/src/main/java/com/interplug/qcast/biz/community/BoardService.java
+++ b/src/main/java/com/interplug/qcast/biz/community/BoardService.java
@@ -1,28 +1,40 @@
package com.interplug.qcast.biz.community;
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.net.URLEncoder;
-import java.nio.charset.StandardCharsets;
-import java.util.HashMap;
-import java.util.Map;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.http.HttpMethod;
-import org.springframework.stereotype.Service;
-import org.springframework.util.StreamUtils;
-import org.springframework.web.util.UriComponentsBuilder;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.interplug.qcast.biz.community.dto.BoardRequest;
import com.interplug.qcast.biz.community.dto.BoardResponse;
+import com.interplug.qcast.biz.estimate.dto.EstimateApiResponse;
+import com.interplug.qcast.biz.file.dto.FileRequest;
import com.interplug.qcast.config.Exception.ErrorCode;
import com.interplug.qcast.config.Exception.QcastException;
import com.interplug.qcast.config.message.Messages;
import com.interplug.qcast.util.InterfaceQsp;
+import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.io.InputStreamResource;
+import org.springframework.http.*;
+import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
+import org.springframework.stereotype.Service;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.StreamUtils;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.multipart.MultipartHttpServletRequest;
+import org.springframework.web.util.UriComponentsBuilder;
@Slf4j
@Service
@@ -30,8 +42,7 @@ import lombok.extern.slf4j.Slf4j;
public class BoardService {
private final InterfaceQsp interfaceQsp;
- @Autowired
- Messages message;
+ @Autowired Messages message;
@Value("${qsp.url}")
private String QSP_API_URL;
@@ -50,12 +61,14 @@ public class BoardService {
/* [0]. Validation Check */
if ("".equals(boardRequest.getSchNoticeClsCd())) {
// [msg] {0} is required input value.
- throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
+ throw new QcastException(
+ ErrorCode.INVALID_INPUT_VALUE,
message.getMessage("common.message.required.data", "Notice Cls Code"));
}
if ("".equals(boardRequest.getSchNoticeTpCd())) {
// [msg] {0} is required input value.
- throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
+ throw new QcastException(
+ ErrorCode.INVALID_INPUT_VALUE,
message.getMessage("common.message.required.data", "Notice Type Code"));
}
@@ -65,13 +78,39 @@ public class BoardService {
encodedSchTitle = URLEncoder.encode(boardRequest.getSchTitle(), StandardCharsets.UTF_8);
}
- String url = QSP_API_URL + "/api/board/list";
- String apiUrl = UriComponentsBuilder.fromHttpUrl(url)
- .queryParam("noticeNo", boardRequest.getNoticeNo()).queryParam("schTitle", encodedSchTitle)
- .queryParam("schNoticeTpCd", boardRequest.getSchNoticeTpCd())
- .queryParam("schNoticeClsCd", boardRequest.getSchNoticeClsCd())
- .queryParam("startRow", boardRequest.getStartRow())
- .queryParam("endRow", boardRequest.getEndRow()).build().toUriString();
+ String url = null;
+ String apiUrl = null;
+
+ if("QNA".equals(boardRequest.getSchNoticeClsCd())) {
+ url = QSP_API_URL + "/api/qna/list";
+ apiUrl = UriComponentsBuilder.fromHttpUrl(url)
+ .queryParam("compCd", boardRequest.getCompCd())
+ .queryParam("storeId", boardRequest.getStoreId())
+ .queryParam("siteTpCd", boardRequest.getSiteTpCd())
+ .queryParam("schTitle", encodedSchTitle)
+ .queryParam("schRegId", boardRequest.getSchRegId())
+ .queryParam("schFromDt", boardRequest.getSchFromDt())
+ .queryParam("schToDt", boardRequest.getSchToDt())
+ .queryParam("startRow", boardRequest.getStartRow())
+ .queryParam("endRow", boardRequest.getEndRow())
+ .queryParam("loginId", boardRequest.getLoginId())
+ .queryParam("langCd", boardRequest.getLangCd())
+ .build()
+ .toUriString();
+
+ } else {
+ url = QSP_API_URL + "/api/board/list";
+ apiUrl = UriComponentsBuilder.fromHttpUrl(url)
+ .queryParam("noticeNo", boardRequest.getNoticeNo())
+ .queryParam("schTitle", encodedSchTitle)
+ .queryParam("schNoticeTpCd", boardRequest.getSchNoticeTpCd())
+ .queryParam("schNoticeClsCd", boardRequest.getSchNoticeClsCd())
+ .queryParam("startRow", boardRequest.getStartRow())
+ .queryParam("endRow", boardRequest.getEndRow())
+ .queryParam("schMainYn", boardRequest.getSchMainYn())
+ .build()
+ .toUriString();
+ }
/* [2]. QSP API CALL -> Response */
String strResponse = interfaceQsp.callApi(HttpMethod.GET, apiUrl, null);
@@ -103,15 +142,32 @@ public class BoardService {
/* [0]. Validation Check */
if (boardRequest.getNoticeNo() == null) {
// [msg] {0} is required input value.
- throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
+ throw new QcastException(
+ ErrorCode.INVALID_INPUT_VALUE,
message.getMessage("common.message.required.data", "Notice No"));
}
- /* [1]. QSP API (url + param) Setting */
- String url = QSP_API_URL + "/api/board/detail";
- String apiUrl = UriComponentsBuilder.fromHttpUrl(url)
- .queryParam("noticeNo", boardRequest.getNoticeNo()).build().toUriString();
+ String url = null;
+ String apiUrl = null;
+ if("QNA".equals(boardRequest.getSchNoticeClsCd())) {
+ url = QSP_API_URL + "/api/qna/detail";
+ apiUrl = UriComponentsBuilder.fromHttpUrl(url)
+ .queryParam("qnaNo", boardRequest.getQnaNo())
+ .queryParam("compCd", boardRequest.getCompCd())
+ .queryParam("loginId", boardRequest.getLoginId())
+ .queryParam("langCd", boardRequest.getLangCd())
+ .queryParam("siteTpCd", boardRequest.getSiteTpCd())
+ .build()
+ .toUriString();
+ } else {
+ /* [1]. QSP API (url + param) Setting */
+ url = QSP_API_URL + "/api/board/detail";
+ apiUrl = UriComponentsBuilder.fromHttpUrl(url)
+ .queryParam("noticeNo", boardRequest.getNoticeNo())
+ .build()
+ .toUriString();
+ }
/* [2]. QSP API CALL -> Response */
String strResponse = interfaceQsp.callApi(HttpMethod.GET, apiUrl, null);
@@ -128,6 +184,138 @@ public class BoardService {
return response;
}
+ /**
+ * ※ 게시판 저장 QSP -> Q.CAST3
+ *
+ * @param boardRequest
+ * @return
+ * @throws Exception
+ */
+ public BoardResponse getBoardQnaSave(BoardRequest boardRequest, HttpServletRequest request) throws Exception {
+
+
+
+ BoardResponse response = null;
+
+ /* [0]. Validation Check */
+ if (boardRequest.getCompCd() == null || boardRequest.getCompCd().isEmpty()) {
+ // [msg] {0} is required input value.
+ throw new QcastException(
+ ErrorCode.INVALID_INPUT_VALUE,
+ message.getMessage("common.message.required.data", "Comp Cd"));
+ }
+ if (boardRequest.getSiteTpCd() == null || boardRequest.getSiteTpCd().isEmpty()) {
+ // [msg] {0} is required input value.
+ throw new QcastException(
+ ErrorCode.INVALID_INPUT_VALUE,
+ message.getMessage("common.message.required.data", "Site Type Cd"));
+ }
+ if (boardRequest.getQnaClsLrgCd() == null || boardRequest.getQnaClsLrgCd().isEmpty()) {
+ // [msg] {0} is required input value.
+ throw new QcastException(
+ ErrorCode.INVALID_INPUT_VALUE,
+ message.getMessage("common.message.required.data", "Qna Cls Large Cd"));
+ }
+ if (boardRequest.getQnaClsMidCd() == null || boardRequest.getQnaClsMidCd().isEmpty()) {
+ // [msg] {0} is required input value.
+ throw new QcastException(
+ ErrorCode.INVALID_INPUT_VALUE,
+ message.getMessage("common.message.required.data", "Qna Cls Mid Cd"));
+ }
+ if (boardRequest.getTitle() == null || boardRequest.getTitle().isEmpty()) {
+ // [msg] {0} is required input value.
+ throw new QcastException(
+ ErrorCode.INVALID_INPUT_VALUE,
+ message.getMessage("common.message.required.data", "title"));
+ }
+
+ if (boardRequest.getContents() == null || boardRequest.getContents().isEmpty()) {
+ // [msg] {0} is required input value.
+ throw new QcastException(
+ ErrorCode.INVALID_INPUT_VALUE,
+ message.getMessage("common.message.required.data", "contents"));
+ }
+
+ if (boardRequest.getRegId() == null || boardRequest.getRegId().isEmpty()) {
+ // [msg] {0} is required input value.
+ throw new QcastException(
+ ErrorCode.INVALID_INPUT_VALUE,
+ message.getMessage("common.message.required.data", "Reg Id"));
+ }
+
+ if (boardRequest.getRegUserNm() == null || boardRequest.getRegUserNm().isEmpty()) {
+ // [msg] {0} is required input value.
+ throw new QcastException(
+ ErrorCode.INVALID_INPUT_VALUE,
+ message.getMessage("common.message.required.data", "Reg User Nm"));
+ }
+
+ String url = null;
+ String apiUrl = null;
+BoardResponse boardResponse = null;
+ if("QNA".equals(boardRequest.getSchNoticeClsCd())) {
+ url = QSP_API_URL + "/api/qna/save";
+ apiUrl = UriComponentsBuilder.fromHttpUrl(url)
+ .queryParam("siteTpCd", boardRequest.getSiteTpCd())
+ .queryParam("compCd", boardRequest.getCompCd())
+ .queryParam("regId", boardRequest.getRegId())
+ .queryParam("title", boardRequest.getTitle())
+ .queryParam("qnaClsLrgCd", boardRequest.getQnaClsLrgCd() )
+ .queryParam("qnaClsMidCd", boardRequest.getQnaClsMidCd())
+ .queryParam("qnaClsSmlCd", boardRequest.getQnaClsSmlCd())
+ .queryParam("contents", boardRequest.getContents())
+ .queryParam("storeId", boardRequest.getStoreId())
+ .queryParam("regUserNm", boardRequest.getRegUserNm())
+ .queryParam("regUserTelNo", boardRequest.getRegUserTelNo())
+ .queryParam("qstMail", boardRequest.getQstMail())
+// .queryParam("files", multipartFileList)
+ .build()
+ .toUriString();
+ }
+
+ List multipartFileList = new ArrayList<>();
+ MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
+ Map> mapMultipart = multipartRequest.getMultiFileMap();
+
+
+ String strParamKey;
+ for (String s : mapMultipart.keySet()) {
+ strParamKey = s;
+ multipartFileList.addAll(mapMultipart.get(strParamKey));
+ }
+
+
+ LinkedMultiValueMap map = new LinkedMultiValueMap<>();
+ RestTemplate restTemplate = new RestTemplate();
+ String responseStr;
+ HttpStatus httpStatus = HttpStatus.CREATED;
+
+ for (MultipartFile file : multipartFileList) {
+ if (!file.isEmpty()) {
+ map.add("file", new MultipartInputStreamFileResource(file.getInputStream(), file.getOriginalFilename()));
+ }
+ }
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.MULTIPART_FORM_DATA);
+
+ HttpEntity> requestEntity = new HttpEntity<>(map, headers);
+ String strResponse = restTemplate.postForObject(apiUrl, requestEntity, String.class);
+
+ //String strResponse = interfaceQsp.callApi(HttpMethod.POST, apiUrl, multipartFileList);
+
+ if (!"".equals(strResponse)) {
+
+ com.fasterxml.jackson.databind.ObjectMapper om =
+ new com.fasterxml.jackson.databind.ObjectMapper()
+ .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ response = om.readValue(strResponse, BoardResponse.class);
+ } else {
+ // [msg] No data
+ throw new QcastException(ErrorCode.NOT_FOUND, message.getMessage("common.message.no.data"));
+ }
+
+ return response;
+ }
/**
* ※ 게시판 파일 다운로드 QSP -> Q.CAST3
*
@@ -135,17 +323,33 @@ public class BoardService {
* @param encodeFileNo
* @throws Exception
*/
- public void getFileDownload(HttpServletResponse response, String encodeFileNo) throws Exception {
+ public void getFileDownload(HttpServletResponse response, String keyNo, String zipYn)
+ throws Exception {
/* [0]. Validation Check */
- if ("".equals(encodeFileNo)) {
+ if ("".equals(keyNo)) {
// [msg] {0} is required input value.
- throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
- message.getMessage("common.message.required.data", "File No"));
+ String arg = "Y".equals(zipYn) ? "Notice No" : "File No";
+ throw new QcastException(
+ ErrorCode.INVALID_INPUT_VALUE, message.getMessage("common.message.required.data", arg));
}
/* [1]. QSP API (url + fileNo) Setting */
- String url = QSP_API_URL + "/api/file/downloadFile" + "?encodeFileNo=" + encodeFileNo;
+ String url = null;
+ if("NO".equals(zipYn)) {
+ url = QSP_API_URL + "/api/file/downloadFile2?encodeFileNo=" + keyNo;
+
+ }else{
+ url = QSP_API_URL + "/api/file/downloadFile";
+
+ if ("Y".equals(zipYn)) {
+ url += "?noticeNo=" + keyNo;
+ } else {
+ url += "?fileNo=" + keyNo;
+ }
+ }
+
+
/* [2]. QSP API CALL -> Response */
Map result = new HashMap();
@@ -154,10 +358,13 @@ public class BoardService {
if (byteResponse != null && byteResponse.length > 0) {
try {
/* [3]. API 응답 파일 처리 */
+ response.addHeader("Access-Control-Expose-Headers", "Content-Disposition");
response.setContentType(
- !"".equals(result.get("type")) && result.get("type") != null ? result.get("type")
+ !"".equals(result.get("type")) && result.get("type") != null
+ ? result.get("type")
: "application/octet-stream");
- response.setHeader("Content-Disposition",
+ response.setHeader(
+ "Content-Disposition",
!"".equals(result.get("disposition")) && result.get("disposition") != null
? result.get("disposition")
: "attachment;");
@@ -170,14 +377,35 @@ public class BoardService {
} catch (Exception e) {
// [msg] File download error
- throw new QcastException(ErrorCode.INTERNAL_SERVER_ERROR,
+ throw new QcastException(
+ ErrorCode.INTERNAL_SERVER_ERROR,
message.getMessage("common.message.file.download.error"));
}
} else {
// [msg] File does not exist.
- throw new QcastException(ErrorCode.NOT_FOUND,
- message.getMessage("common.message.file.download.exists"));
+ throw new QcastException(
+ ErrorCode.NOT_FOUND, message.getMessage("common.message.file.download.exists"));
}
}
}
+
+class MultipartInputStreamFileResource extends InputStreamResource {
+
+ private final String filename;
+
+ MultipartInputStreamFileResource(InputStream inputStream, String filename) {
+ super(inputStream);
+ this.filename = filename;
+ }
+
+ @Override
+ public String getFilename() {
+ return this.filename;
+ }
+
+ @Override
+ public long contentLength() throws IOException {
+ return -1; // we do not want to generally read the whole stream into memory ...
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/interplug/qcast/biz/community/dto/BoardRequest.java b/src/main/java/com/interplug/qcast/biz/community/dto/BoardRequest.java
index 1823be75..96beafc1 100644
--- a/src/main/java/com/interplug/qcast/biz/community/dto/BoardRequest.java
+++ b/src/main/java/com/interplug/qcast/biz/community/dto/BoardRequest.java
@@ -1,7 +1,13 @@
package com.interplug.qcast.biz.community.dto;
+import com.interplug.qcast.biz.file.dto.FileRequest;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.servlet.http.HttpServletRequest;
import lombok.Getter;
import lombok.Setter;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.List;
@Getter
@Setter
@@ -25,4 +31,40 @@ public class BoardRequest {
/** 종료 행 */
private int endRow;
+ /** 메인여부 */
+ private String schMainYn;
+
+ //Company Code
+ private String compCd;
+ private String storeId;
+ //Site Type Code
+ private String siteTpCd;
+ private String loginId;
+
+ //등록자 아이디
+ private String schRegId;
+ //검색 시작일시
+ private String schFromDt;
+ //검색 끝일시
+ private String schToDt;
+
+ private String qnaNo;
+
+ private String langCd;
+
+ private String qnaClsLrgCd;
+ private String qnaClsMidCd;
+ private String qnaClsSmlCd;
+ private String contents;
+ private String regId;
+ private String title;
+ private String regUserNm;
+ private String regUserTelNo;
+ private String qstMail;
+
+
+ @Schema(description = "첨부파일 목록")
+ List fileList;
+
+
}
diff --git a/src/main/java/com/interplug/qcast/biz/displayItem/DisplayItemController.java b/src/main/java/com/interplug/qcast/biz/displayItem/DisplayItemController.java
index 71025bca..47543967 100644
--- a/src/main/java/com/interplug/qcast/biz/displayItem/DisplayItemController.java
+++ b/src/main/java/com/interplug/qcast/biz/displayItem/DisplayItemController.java
@@ -1,14 +1,23 @@
package com.interplug.qcast.biz.displayItem;
import com.interplug.qcast.biz.displayItem.dto.DisplayItemRequest;
+import com.interplug.qcast.biz.displayItem.dto.ItemDetailResponse;
+import com.interplug.qcast.biz.displayItem.dto.ItemRequest;
import com.interplug.qcast.biz.displayItem.dto.ItemResponse;
+import com.interplug.qcast.config.Exception.QcastException;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@@ -18,26 +27,46 @@ import org.springframework.web.bind.annotation.*;
public class DisplayItemController {
private final DisplayItemService displayItemService;
+ /**
+ * 전시제품 정보 등록/수정
+ *
+ * @param displayItemRequest
+ * @throws QcastException
+ */
@Operation(description = "전시제품 정보를 등록/수정한다. (동기화)")
@PostMapping("/display-item-save")
@ResponseStatus(HttpStatus.OK)
public void setStoreDisplayItemSave(@RequestBody DisplayItemRequest displayItemRequest)
- throws Exception {
+ throws QcastException {
displayItemService.setStoreDisplayItemSave(displayItemRequest);
}
+ /**
+ * 제품 목록 조회
+ *
+ * @param itemRequest
+ * @return
+ * @throws QcastException
+ */
@Operation(description = "제품 목록을 조회한다.")
- @GetMapping("/item-list")
+ @PostMapping("/item-list")
@ResponseStatus(HttpStatus.OK)
- public List getItemList(@RequestParam("saleStoreId") String saleStoreId)
- throws Exception {
- return displayItemService.getItemList(saleStoreId);
+ public List getItemList(@RequestBody ItemRequest itemRequest)
+ throws QcastException {
+ return displayItemService.getItemList(itemRequest);
}
+ /**
+ * 제품 상세 정보 조회
+ *
+ * @param itemId
+ * @return
+ * @throws QcastException
+ */
@Operation(description = "제품 상세 정보를 조회한다.")
@GetMapping("/item-detail")
@ResponseStatus(HttpStatus.OK)
- public ItemResponse getItemDetail(@RequestParam("itemId") String itemId) throws Exception {
+ public ItemDetailResponse getItemDetail(@RequestParam String itemId) throws QcastException {
return displayItemService.getItemDetail(itemId);
}
}
diff --git a/src/main/java/com/interplug/qcast/biz/displayItem/DisplayItemMapper.java b/src/main/java/com/interplug/qcast/biz/displayItem/DisplayItemMapper.java
index b553dc1e..0959b8c3 100644
--- a/src/main/java/com/interplug/qcast/biz/displayItem/DisplayItemMapper.java
+++ b/src/main/java/com/interplug/qcast/biz/displayItem/DisplayItemMapper.java
@@ -1,8 +1,6 @@
package com.interplug.qcast.biz.displayItem;
-import com.interplug.qcast.biz.displayItem.dto.DisplayItemRequest;
-import com.interplug.qcast.biz.displayItem.dto.ItemResponse;
-import com.interplug.qcast.biz.displayItem.dto.ItemSyncResponse;
+import com.interplug.qcast.biz.displayItem.dto.*;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@@ -12,9 +10,11 @@ public interface DisplayItemMapper {
void setStoreDisplayItemSave(DisplayItemRequest displayItemRequest) throws Exception;
- List getItemList(@Param("saleStoreId") String saleStoreId);
+ List getItemList(ItemRequest itemRequest);
- ItemResponse getItemDetail(@Param("itemId") String itemId);
+ ItemDetailResponse getItemDetail(@Param("itemId") String itemId);
+
+ List selectItemBomList(@Param("itemId") String itemId);
/**
* 아이템 정보 동기화
@@ -24,4 +24,40 @@ public interface DisplayItemMapper {
* @throws Exception
*/
int setItemSyncSave(ItemSyncResponse itemInfoSync);
+
+ /**
+ * 아이템 추가 정보 동기화
+ *
+ * @param itemInfoSync 아이템 정보
+ * @return
+ * @throws Exception
+ */
+ int setItemInfoSyncSave(ItemSyncResponse itemInfoSync);
+
+ /**
+ * BOM 아이템 정보 삭제
+ *
+ * @param bomInfoSync 아이템 정보
+ * @return
+ * @throws Exception
+ */
+ int deleteBomSync(BomSyncResponse bomInfoSync);
+
+ /**
+ * BOM 아이템 정보 동기화
+ *
+ * @param bomInfoSync 아이템 정보
+ * @return
+ * @throws Exception
+ */
+ int setBomSyncSave(BomSyncResponse bomInfoSync);
+
+ /**
+ * 아이템 가격 정보 동기화
+ *
+ * @param priceItemSyncResponse 아이템 가격 정보
+ * @return
+ * @throws Exception
+ */
+ int setPriceItemSyncSave(PriceItemSyncResponse priceItemSyncResponse);
}
diff --git a/src/main/java/com/interplug/qcast/biz/displayItem/DisplayItemService.java b/src/main/java/com/interplug/qcast/biz/displayItem/DisplayItemService.java
index d9e6a568..7cd19a56 100644
--- a/src/main/java/com/interplug/qcast/biz/displayItem/DisplayItemService.java
+++ b/src/main/java/com/interplug/qcast/biz/displayItem/DisplayItemService.java
@@ -1,11 +1,11 @@
package com.interplug.qcast.biz.displayItem;
-import com.interplug.qcast.biz.displayItem.dto.DisplayItemRequest;
-import com.interplug.qcast.biz.displayItem.dto.ItemResponse;
-import com.interplug.qcast.biz.displayItem.dto.ItemSyncResponse;
+import com.interplug.qcast.biz.displayItem.dto.*;
import com.interplug.qcast.biz.estimate.EstimateMapper;
import com.interplug.qcast.biz.estimate.dto.NoteRequest;
import com.interplug.qcast.biz.estimate.dto.NoteResponse;
+import com.interplug.qcast.config.Exception.ErrorCode;
+import com.interplug.qcast.config.Exception.QcastException;
import io.micrometer.common.util.StringUtils;
import java.util.List;
import lombok.RequiredArgsConstructor;
@@ -25,20 +25,29 @@ public class DisplayItemService {
* 판매점 노출 아이템 정보 저장
*
* @param displayItemRequest
- * @throws Exception
+ * @throws QcastException
*/
- public void setStoreDisplayItemSave(DisplayItemRequest displayItemRequest) throws Exception {
- displayItemMapper.setStoreDisplayItemSave(displayItemRequest);
+ public void setStoreDisplayItemSave(DisplayItemRequest displayItemRequest) throws QcastException {
+ try {
+ displayItemMapper.setStoreDisplayItemSave(displayItemRequest);
+ } catch (Exception e) {
+ throw new QcastException(ErrorCode.INTERNAL_SERVER_ERROR, e.getMessage());
+ }
}
/**
* 아이템 목록 조회
*
- * @param saleStoreId
+ * @param itemRequest
* @return
+ * @throws QcastException
*/
- public List getItemList(String saleStoreId) {
- return displayItemMapper.getItemList(saleStoreId);
+ public List getItemList(ItemRequest itemRequest) throws QcastException {
+ try {
+ return displayItemMapper.getItemList(itemRequest);
+ } catch (Exception e) {
+ throw new QcastException(ErrorCode.INTERNAL_SERVER_ERROR, e.getMessage());
+ }
}
/**
@@ -47,29 +56,39 @@ public class DisplayItemService {
* @param itemId
* @return
*/
- public ItemResponse getItemDetail(String itemId) {
+ public ItemDetailResponse getItemDetail(String itemId) throws QcastException {
+ try {
+ ItemDetailResponse itemDetailResponse = displayItemMapper.getItemDetail(itemId);
- ItemResponse itemResponse = displayItemMapper.getItemDetail(itemId);
+ if (itemDetailResponse != null) {
+ // BOM 헤더 아이템인 경우 BOM List를 내려준다.
+ if ("ERLA".equals(itemDetailResponse.getItemCtgGr())) {
+ List itemBomList = displayItemMapper.selectItemBomList(itemId);
- if (itemResponse != null) {
- // 견적특이사항 관련 데이터 셋팅
- String[] arrItemId = {itemId};
+ itemDetailResponse.setItemBomList(itemBomList);
+ }
- NoteRequest noteRequest = new NoteRequest();
- noteRequest.setArrItemId(arrItemId);
- noteRequest.setSchSpnTypeCd("PROD");
- List noteItemList = estimateMapper.selectEstimateNoteItemList(noteRequest);
+ // 견적특이사항 관련 데이터 셋팅
+ String[] arrItemId = {itemId};
- String spnAttrCds = "";
- for (NoteResponse noteResponse : noteItemList) {
- spnAttrCds += !StringUtils.isEmpty(spnAttrCds) ? "、" : "";
- spnAttrCds += noteResponse.getCode();
+ NoteRequest noteRequest = new NoteRequest();
+ noteRequest.setArrItemId(arrItemId);
+ noteRequest.setSchSpnTypeCd("PROD");
+ List noteItemList = estimateMapper.selectEstimateNoteItemList(noteRequest);
+
+ String spnAttrCds = "";
+ for (NoteResponse noteResponse : noteItemList) {
+ spnAttrCds += !StringUtils.isEmpty(spnAttrCds) ? "、" : "";
+ spnAttrCds += noteResponse.getCode();
+ }
+
+ itemDetailResponse.setSpnAttrCds(spnAttrCds);
}
- itemResponse.setSpnAttrCds(spnAttrCds);
+ return itemDetailResponse;
+ } catch (Exception e) {
+ throw new QcastException(ErrorCode.INTERNAL_SERVER_ERROR, e.getMessage());
}
-
- return itemResponse;
}
/**
@@ -82,8 +101,69 @@ public class DisplayItemService {
public int setItemSyncSave(List itemSyncList) throws Exception {
int cnt = 0;
for (ItemSyncResponse itemSyncData : itemSyncList) {
- displayItemMapper.setItemSyncSave(itemSyncData);
+ cnt += displayItemMapper.setItemSyncSave(itemSyncData);
+ cnt += displayItemMapper.setItemInfoSyncSave(itemSyncData);
}
return cnt;
}
+
+ /**
+ * 아이템 정보 동기화
+ *
+ * @param bomSyncList 아이템 목록
+ * @return
+ * @throws Exception
+ */
+ public int setBomSyncSave(List bomSyncList) throws Exception {
+ int cnt = 0;
+ // 2026-05-12 같은 키(PACKAGE_ITEM_ID, ITEM_ID)에 A/D 가 함께 오는 케이스 대비,
+ // 외부 응답 순서와 무관하게 결정적 결과(A 가 이김)를 보장하기 위해 2단계로 처리.
+ // 1단계: D 먼저 전부 DELETE
+ for (BomSyncResponse bomSyncData : bomSyncList) {
+ if ("D".equals(bomSyncData.getStatCd())) {
+ // Bom 아이템 삭제 처리
+ cnt += displayItemMapper.deleteBomSync(bomSyncData);
+ }
+ }
+ // 2단계: D 외 전부 MERGE (upsert)
+ for (BomSyncResponse bomSyncData : bomSyncList) {
+ if (!"D".equals(bomSyncData.getStatCd())) {
+ // Bom 아이템 수정/등록 처리
+ cnt += displayItemMapper.setBomSyncSave(bomSyncData);
+ }
+ }
+ return cnt;
+ }
+
+ /**
+ * 아이템 가격 정보 동기화
+ *
+ * @param priceItemSyncList 가격 목록
+ * @return
+ * @throws Exception
+ */
+ public int setPriceSyncSave(List priceItemSyncList) throws Exception {
+ int cnt = 0;
+ for (PriceItemSyncResponse priceItemSyncResponse : priceItemSyncList) {
+ cnt += displayItemMapper.setPriceItemSyncSave(priceItemSyncResponse);
+ }
+ return cnt;
+ }
+
+ /**
+ * 노출 아이템 동기화 - Batch
+ *
+ * @param displayItemList
+ * @throws Exception
+ */
+ public void setStoreDispItemBatch(List displayItemList) throws Exception {
+ // 노출 아이템 동기화
+ for (DisplayItemRequest displayItemRequest : displayItemList) {
+ try {
+ displayItemMapper.setStoreDisplayItemSave(displayItemRequest);
+ } catch (Exception e) {
+ log.error(e.getMessage());
+ }
+ }
+ }
}
diff --git a/src/main/java/com/interplug/qcast/biz/displayItem/dto/BomSyncResponse.java b/src/main/java/com/interplug/qcast/biz/displayItem/dto/BomSyncResponse.java
new file mode 100644
index 00000000..8e109306
--- /dev/null
+++ b/src/main/java/com/interplug/qcast/biz/displayItem/dto/BomSyncResponse.java
@@ -0,0 +1,27 @@
+package com.interplug.qcast.biz.displayItem.dto;
+
+import com.interplug.qcast.util.DefaultResponse;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/** Bom 아이템 동기화 Response */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class BomSyncResponse extends DefaultResponse {
+
+ @Schema(description = "Material Number")
+ String packageItemId;
+
+ @Schema(description = "Bom Code")
+ String itemId;
+
+ @Schema(description = "Amount")
+ String amount;
+
+ @Schema(description = "Manual Flag")
+ String manualFlg;
+
+ @Schema(description = "Status Code(A, D)")
+ String statCd;
+}
diff --git a/src/main/java/com/interplug/qcast/biz/displayItem/dto/ItemDetailResponse.java b/src/main/java/com/interplug/qcast/biz/displayItem/dto/ItemDetailResponse.java
new file mode 100644
index 00000000..1ec523e8
--- /dev/null
+++ b/src/main/java/com/interplug/qcast/biz/displayItem/dto/ItemDetailResponse.java
@@ -0,0 +1,57 @@
+package com.interplug.qcast.biz.displayItem.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import java.util.List;
+import lombok.Data;
+
+@Data
+public class ItemDetailResponse {
+
+ @Schema(description = "Itme Id")
+ private String itemId;
+
+ @Schema(description = "Item No")
+ private String itemNo;
+
+ @Schema(description = "Item Name")
+ private String itemName;
+
+ @Schema(description = "Goods No")
+ private String goodsNo;
+
+ @Schema(description = "Unit")
+ private String unit;
+
+ @Schema(description = "Specification")
+ private String specification;
+
+ @Schema(description = "pnow_w")
+ private String pnowW;
+
+ @Schema(description = "Item Group")
+ private String itemGroup;
+
+ @Schema(description = "Module Flag")
+ private String moduleFlg;
+
+ @Schema(description = "PKG Material Flag")
+ private String pkgMaterialFlg;
+
+ @Schema(description = "File Upload Flag")
+ private String fileUploadFlg;
+
+ @Schema(description = "오픈가 Flag")
+ private String openFlg;
+
+ @Schema(description = "Sale Price")
+ private String salePrice;
+
+ @Schema(description = "Item Ctg Group")
+ private String itemCtgGr;
+
+ @Schema(description = "견적 특이사항 코드")
+ private String spnAttrCds;
+
+ @Schema(description = "Bom 목록")
+ List itemBomList;
+}
diff --git a/src/main/java/com/interplug/qcast/biz/displayItem/dto/ItemRequest.java b/src/main/java/com/interplug/qcast/biz/displayItem/dto/ItemRequest.java
new file mode 100644
index 00000000..0915f14b
--- /dev/null
+++ b/src/main/java/com/interplug/qcast/biz/displayItem/dto/ItemRequest.java
@@ -0,0 +1,17 @@
+package com.interplug.qcast.biz.displayItem.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+public class ItemRequest {
+
+ @Schema(description = "Sale Store Id")
+ private String saleStoreId;
+
+ @Schema(description = "Cold Zone Flg")
+ private String coldZoneFlg;
+
+ @Schema(description = "Salt-affected Flg")
+ private String saltAffectedFlg;
+}
diff --git a/src/main/java/com/interplug/qcast/biz/displayItem/dto/ItemResponse.java b/src/main/java/com/interplug/qcast/biz/displayItem/dto/ItemResponse.java
index 72814127..53ea1468 100644
--- a/src/main/java/com/interplug/qcast/biz/displayItem/dto/ItemResponse.java
+++ b/src/main/java/com/interplug/qcast/biz/displayItem/dto/ItemResponse.java
@@ -9,6 +9,9 @@ public class ItemResponse {
@Schema(description = "Itme Id")
private String itemId;
+ @Schema(description = "Item No")
+ private String itemNo;
+
@Schema(description = "Item Name")
private String itemName;
@@ -39,6 +42,12 @@ public class ItemResponse {
@Schema(description = "Sale Price")
private String salePrice;
+ @Schema(description = "Item Ctg Group")
+ private String itemCtgGr;
+
+ @Schema(description = "Bom Amount")
+ private String bomAmount;
+
@Schema(description = "견적 특이사항 코드")
private String spnAttrCds;
}
diff --git a/src/main/java/com/interplug/qcast/biz/displayItem/dto/ItemSyncResponse.java b/src/main/java/com/interplug/qcast/biz/displayItem/dto/ItemSyncResponse.java
index cfd52ce4..df303391 100644
--- a/src/main/java/com/interplug/qcast/biz/displayItem/dto/ItemSyncResponse.java
+++ b/src/main/java/com/interplug/qcast/biz/displayItem/dto/ItemSyncResponse.java
@@ -3,8 +3,10 @@ package com.interplug.qcast.biz.displayItem.dto;
import com.interplug.qcast.util.DefaultResponse;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
+import lombok.EqualsAndHashCode;
/** 아이템 동기화 Response */
+@EqualsAndHashCode(callSuper = false)
@Data
public class ItemSyncResponse extends DefaultResponse {
@Schema(description = "Material Number")
@@ -34,6 +36,9 @@ public class ItemSyncResponse extends DefaultResponse {
@Schema(description = "Power Class")
String pnowW;
+ @Schema(description = "Item Ctg Group")
+ String itemCtgGr;
+
@Schema(description = "Qcast Material Group Code")
String itemGroup;
@@ -133,6 +138,18 @@ public class ItemSyncResponse extends DefaultResponse {
@Schema(description = "PKG Material Flg")
String pkgMaterialFlg;
+ @Schema(description = "Temp Loss")
+ String tempLoss;
+
+ @Schema(description = "Temperature Coefficient")
+ String tempCoeff;
+
+ @Schema(description = "AMP")
+ String amp;
+
+ @Schema(description = "Conversion Efficiency")
+ String cnvEff;
+
@Schema(description = "Status Code(A, D)")
String statCd;
diff --git a/src/main/java/com/interplug/qcast/biz/displayItem/dto/PriceItemSyncResponse.java b/src/main/java/com/interplug/qcast/biz/displayItem/dto/PriceItemSyncResponse.java
new file mode 100644
index 00000000..c7d33b68
--- /dev/null
+++ b/src/main/java/com/interplug/qcast/biz/displayItem/dto/PriceItemSyncResponse.java
@@ -0,0 +1,23 @@
+package com.interplug.qcast.biz.displayItem.dto;
+
+import com.interplug.qcast.util.DefaultResponse;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/** 아이템 동기화 Response */
+@EqualsAndHashCode(callSuper = false)
+@Data
+public class PriceItemSyncResponse extends DefaultResponse {
+ @Schema(description = "Price Pattern(정가 : 510,.A가격 : 513, C가격 : 514)")
+ String pricePattern;
+
+ @Schema(description = "Material Number")
+ String itemId;
+
+ @Schema(description = "Price Amount")
+ String salePrice;
+
+ @Schema(description = "Price Amount")
+ String costPrice;
+}
diff --git a/src/main/java/com/interplug/qcast/biz/displayItem/dto/PriceSyncRequest.java b/src/main/java/com/interplug/qcast/biz/displayItem/dto/PriceSyncRequest.java
new file mode 100644
index 00000000..7c5dae63
--- /dev/null
+++ b/src/main/java/com/interplug/qcast/biz/displayItem/dto/PriceSyncRequest.java
@@ -0,0 +1,11 @@
+package com.interplug.qcast.biz.displayItem.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/** 가격 동기화 Request */
+@Data
+public class PriceSyncRequest {
+ @Schema(description = "All Material Master Yn")
+ private String allYn;
+}
diff --git a/src/main/java/com/interplug/qcast/biz/displayItem/dto/PriceSyncResponse.java b/src/main/java/com/interplug/qcast/biz/displayItem/dto/PriceSyncResponse.java
new file mode 100644
index 00000000..afcbdefa
--- /dev/null
+++ b/src/main/java/com/interplug/qcast/biz/displayItem/dto/PriceSyncResponse.java
@@ -0,0 +1,19 @@
+package com.interplug.qcast.biz.displayItem.dto;
+
+import com.interplug.qcast.util.DefaultResponse;
+import io.swagger.v3.oas.annotations.media.Schema;
+import java.util.List;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/** 가격 동기화 Response */
+@EqualsAndHashCode(callSuper = false)
+@Data
+public class PriceSyncResponse extends DefaultResponse {
+
+ @Schema(description = "Module Price")
+ private List modulePriceList;
+
+ @Schema(description = "Bos Price")
+ private List bosPriceList;
+}
diff --git a/src/main/java/com/interplug/qcast/biz/estimate/EstimateController.java b/src/main/java/com/interplug/qcast/biz/estimate/EstimateController.java
index 2440cd7d..38978c47 100644
--- a/src/main/java/com/interplug/qcast/biz/estimate/EstimateController.java
+++ b/src/main/java/com/interplug/qcast/biz/estimate/EstimateController.java
@@ -1,6 +1,7 @@
package com.interplug.qcast.biz.estimate;
import com.interplug.qcast.biz.estimate.dto.*;
+import com.interplug.qcast.biz.object.dto.ObjectRequest;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
@@ -22,18 +23,25 @@ public class EstimateController {
@Operation(description = "1차점 가격 관리 목록을 조회한다.")
@GetMapping("/price/store-price-list")
@ResponseStatus(HttpStatus.OK)
- public PriceResponse selectStorePriceList(PriceRequest priceRequest) throws Exception {
+ public EstimateApiResponse selectStorePriceList(PriceRequest priceRequest) throws Exception {
return estimateService.selectStorePriceList(priceRequest);
}
@Operation(description = "아이템 가격 목록을 조회한다.")
@PostMapping("/price/item-price-list")
@ResponseStatus(HttpStatus.OK)
- public PriceResponse selectItemPriceList(@RequestBody PriceRequest priceRequest)
+ public EstimateApiResponse selectItemPriceList(@RequestBody PriceRequest priceRequest)
throws Exception {
return estimateService.selectItemPriceList(priceRequest);
}
+ @Operation(description = "견적서 특이사항 타이틀 목록을 조회한다.")
+ @GetMapping("/special-note-title-list")
+ @ResponseStatus(HttpStatus.OK)
+ public List selectSpecialNoteTitleList(NoteRequest noteRequest) throws Exception {
+ return estimateService.selectSpecialNoteTitleList(noteRequest);
+ }
+
@Operation(description = "견적서 특이사항 목록을 조회한다.")
@GetMapping("/special-note-list")
@ResponseStatus(HttpStatus.OK)
@@ -44,7 +52,7 @@ public class EstimateController {
@Operation(description = "견적서 상세를 조회한다.")
@GetMapping("/{objectNo}/{planNo}/detail")
@ResponseStatus(HttpStatus.OK)
- public EstimateResponse selectObjectDetail(
+ public EstimateResponse selectEstimateDetail(
@PathVariable String objectNo, @PathVariable String planNo) throws Exception {
return estimateService.selectEstimateDetail(objectNo, planNo);
}
@@ -59,9 +67,24 @@ public class EstimateController {
@Operation(description = "견적서를 복사한다.")
@PostMapping("/save-estimate-copy")
@ResponseStatus(HttpStatus.CREATED)
- public EstimateResponse insertEstimateCopy(@RequestBody EstimateRequest estimateRequest)
+ public EstimateResponse insertEstimateCopy(@RequestBody EstimateCopyRequest estimateCopyRequest)
throws Exception {
- return estimateService.insertEstimateCopy(estimateRequest);
+ return estimateService.insertEstimateCopy(estimateCopyRequest);
+ }
+
+ @Operation(description = "견적서를 초기화한다.")
+ @PostMapping("/reset-estimate")
+ @ResponseStatus(HttpStatus.CREATED)
+ public EstimateResponse updateEstimateReset(@RequestBody EstimateRequest estimateRequest)
+ throws Exception {
+ return estimateService.updateEstimateReset(estimateRequest);
+ }
+
+ @Operation(description = "견적서 잠금여부를 저장한다.")
+ @PostMapping("/save-estimate-lock")
+ @ResponseStatus(HttpStatus.CREATED)
+ public void updateEstimateLock(@RequestBody EstimateRequest estimateRequest) throws Exception {
+ estimateService.updateEstimateLock(estimateRequest);
}
@Operation(description = "견적서를 엑셀로 다운로드한다.")
@@ -74,4 +97,32 @@ public class EstimateController {
throws Exception {
estimateService.excelDownload(request, response, estimateRequest);
}
+
+ @Operation(description = "2차점 특가 있는 2nd Agency 목록을 조회한다.")
+ @GetMapping("/agency-cust-list")
+ @ResponseStatus(HttpStatus.OK)
+ public EstimateApiResponse selectAgencyCustList(PriceRequest priceRequest) throws Exception {
+ return estimateService.selectAgencyCustList(priceRequest);
+ }
+
+ @Operation(description = "견적서를 삭제한다.")
+ @PostMapping("/delete-estimate")
+ @ResponseStatus(HttpStatus.CREATED)
+ public void deleteEstimate(@RequestBody EstimateRequest estimateRequest) throws Exception {
+ estimateService.deleteEstimate(estimateRequest);
+ }
+
+ @Operation(description = "발전시뮬레이션 필요 데이터 목록을 조회한다.")
+ @GetMapping("/simulation-estimate-list")
+ @ResponseStatus(HttpStatus.OK)
+ public SimulationEstimateListResponse selectSimulationEstimateList(ObjectRequest objectRequest) throws Exception {
+ return estimateService.selectSimulationEstimateList(objectRequest);
+ }
+
+ @Operation(description = "발전시뮬레이션 필요 데이터 상세 정보를 조회한다.")
+ @GetMapping("/simulation-estimate-info")
+ @ResponseStatus(HttpStatus.OK)
+ public SimulationEstimateResponse selectSimulationEstimateInfo(EstimateRequest estimateRequest) throws Exception {
+ return estimateService.selectSimulationEstimateInfo(estimateRequest);
+ }
}
diff --git a/src/main/java/com/interplug/qcast/biz/estimate/EstimateMapper.java b/src/main/java/com/interplug/qcast/biz/estimate/EstimateMapper.java
index 1f73781f..2a5e56f3 100644
--- a/src/main/java/com/interplug/qcast/biz/estimate/EstimateMapper.java
+++ b/src/main/java/com/interplug/qcast/biz/estimate/EstimateMapper.java
@@ -1,8 +1,10 @@
package com.interplug.qcast.biz.estimate;
import com.interplug.qcast.biz.estimate.dto.*;
-import com.interplug.qcast.biz.object.dto.*;
import java.util.List;
+
+import com.interplug.qcast.biz.object.dto.ObjectRequest;
+import com.interplug.qcast.biz.object.dto.ObjectResponse;
import org.apache.ibatis.annotations.Mapper;
@Mapper
@@ -13,42 +15,156 @@ public interface EstimateMapper {
// 견적서 PDF 상세 확인
public EstimateResponse selectEstimatePdfDetail(EstimateRequest estimateRequest);
+ // 견적서 API 상세 확인
+ public EstimateSendResponse selectEstimateApiDetail(EstimateRequest estimateRequest);
+
+ // 견적서 API 실패 목록 조회
+ public List selectEstimateApiFailList();
+
+ // 견적서 도면 아이템 목록 조회
+ public List selectEstimateDrawingItemList(EstimateRequest estimateRequest);
+
// 견적서 아이템 목록 조회
public List selectEstimateItemList(EstimateRequest estimateRequest);
// 아이템 마스터 목록 조회
public List selectItemMasterList(EstimateRequest estimateRequest);
- // 아이템 마스터 목록 조회
+ // 아이템 마스터 BOM 목록 조회
+ public List selectItemMasterBomList(String itemId);
+
+ // 견적서 지붕재 인증용량 조회
+ public String selectEstimateRoofCertVolKw(EstimateRequest estimateRequest);
+
+ // 견적서 지붕재 목록 조회
+ public List selectEstimateRoofList(EstimateRequest estimateRequest);
+
+ // 견적서 지붕재 아이템 목록 조회
+ public List selectEstimateRoofItemList(EstimateRequest estimateRequest);
+
+ // 견적서 지붕재 PC 목록 조회
+ public List selectEstimateCircuitItemList(EstimateRequest estimateRequest);
+
+ // 견적서 지붕재 용량 목록 조회
+ public List selectEstimateRoofVolList(EstimateRequest estimateRequest);
+
+ // 견적서 특이사항 타이틀 목록 조회
+ public List selectEstimateNoteTitleList(NoteRequest noteRequest);
+
+ // 견적서 특이사항 목록 조회
public List selectEstimateNoteList(NoteRequest noteRequest);
// 아이템 마스터 목록 조회
public List selectEstimateNoteItemList(NoteRequest noteRequest);
+ // 아이템 히스토리 번호 조회
+ public String selectEstimateItemHisNo(EstimateRequest estimateRequest);
+
// 물건정보 수정
public int updateObject(EstimateRequest estimateRequest);
+ // 물건정보 상세 수정
+ public int updateObjectInfo(EstimateRequest estimateRequest);
+
// 견적서 정보 수정
public int updateEstimate(EstimateRequest estimateRequest);
- // 견적서 지붕재 등록
+ // 견적서 상세 정보 수정
+ public int updateEstimateInfo(EstimateRequest estimateRequest);
+
+ // 견적서 정보 초기화
+ public int updateEstimateReset(EstimateRequest estimateRequest);
+
+ // 견적서 상세 정보 초기화
+ public int updateEstimateInfoReset(EstimateRequest estimateRequest);
+
+ // 견적서 잠금 수정
+ public int updateEstimateLock(EstimateRequest estimateRequest);
+
+ // 견적서 API 정보 수정
+ public int updateEstimateApi(EstimateRequest estimateRequest);
+
+ // 견적서 지붕면 등록
public int insertEstimateRoof(RoofRequest roofRequest);
- // 견적서 지붕재 아이템 등록
+ // 견적서 지붕면 아이템 등록
public int insertEstimateRoofItem(ItemRequest itemRequest);
+ // 견적서 지붕면 회로구성 아이템 등록
+ public int insertEstimateCircuitItem(ItemRequest itemRequest);
+
+ // 견적서 도면 아이템 등록
+ public int insertEstimateDrawingItem(ItemRequest itemRequest);
+
// 견적서 아이템 등록
public int insertEstimateItem(ItemRequest itemRequest);
+ // 견적서 아이템 상세 등록
+ public int insertEstimateInfoItem(ItemRequest itemRequest);
+
+ // 견적서 아이템 히스토리 등록
+ public int insertEstimateItemHis(ItemRequest itemRequest);
+
// 견적서 지붕재 목록 삭제(물리 삭제)
public int deleteEstimateRoofList(EstimateRequest estimateRequest);
// 견적서 지붕재 아이템 목록 삭제(물리 삭제)
public int deleteEstimateRoofItemList(EstimateRequest estimateRequest);
+ // 견적서 회로구성 아이템 목록 삭제(물리 삭제)
+ public int deleteEstimateCircuitItemList(EstimateRequest estimateRequest);
+
+ // 견적서 도면 아이템 목록 삭제(물리 삭제)
+ public int deleteEstimateDrawingItemList(EstimateRequest estimateRequest);
+
// 견적서 아이템 목록 삭제(물리 삭제)
public int deleteEstimateItemList(EstimateRequest estimateRequest);
+ // 견적서 아이템 상세 목록 삭제(물리 삭제)
+ public int deleteEstimateInfoItemList(EstimateRequest estimateRequest);
+
// 견적서 복사
- public int insertEstimateCopy(EstimateRequest estimateRequest);
+ public int insertEstimateCopy(EstimateCopyRequest estimateCopyRequest);
+
+ // 견적서 상세 복사
+ public int insertEstimateInfoCopy(EstimateCopyRequest estimateCopyRequest);
+
+ // 견적서 지붕면 복사
+ public int insertEstimateRoofCopy(EstimateCopyRequest estimateCopyRequest);
+
+ // 견적서 지붕면 아이템 복사
+ public int insertEstimateRoofItemCopy(EstimateCopyRequest estimateCopyRequest);
+
+ // 견적서 지붕면 회로구성 아이템 복사
+ public int insertEstimateCircuitItemCopy(EstimateCopyRequest estimateCopyRequest);
+
+ // 견적서 도면 아이템 복사
+ public int insertEstimateDrawingItemCopy(EstimateCopyRequest estimateCopyRequest);
+
+ // Plan 확정 동기화
+ public int updatePlanConfirmSync(PlanSyncResponse planSyncData);
+
+ // 견적서 Q.ORDER 연동 시공타입 조회
+ public String selectEstimateConstructSpecification(EstimateRequest estimateRequest);
+
+ // 견적서 수정일 변경
+ public int updateEstimateLastEditDate(EstimateRequest estimateRequest);
+
+ public int updateEstimateInit(EstimateRequest estimateRequest);
+
+ public int updateEstimateInfoInit(EstimateRequest estimateRequest);
+
+ public int insertCanvasPopupStatusCopy(EstimateCopyRequest estimateCopyRequest);
+
+ // 발전시뮬레이션 물건번호 목록 조회
+ public List selectSimulationEstimateList(ObjectRequest objectRequest);
+
+ // 발전시뮬레이션 지붕재 목록 조회
+ public List selectSimulationEstimateRoofList(EstimateRequest estimateRequest);
+
+ // 발전시뮬레이션 지붕재 아이템 목록 조회
+ public List selectSimulationEstimateRoofModuleList(EstimateRequest estimateRequest);
+
+ // 발전시뮬레이션 지붕재 아이템 목록 조회
+ public List selectSimulationEstimateRoofPcsList(EstimateRequest estimateRequest);
}
diff --git a/src/main/java/com/interplug/qcast/biz/estimate/EstimateService.java b/src/main/java/com/interplug/qcast/biz/estimate/EstimateService.java
index 433318ba..0773d437 100644
--- a/src/main/java/com/interplug/qcast/biz/estimate/EstimateService.java
+++ b/src/main/java/com/interplug/qcast/biz/estimate/EstimateService.java
@@ -1,37 +1,74 @@
package com.interplug.qcast.biz.estimate;
-import com.fasterxml.jackson.databind.DeserializationFeature;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import com.interplug.qcast.biz.canvaspopupstatus.CanvasPopupStatusService;
+import com.interplug.qcast.biz.canvaspopupstatus.dto.CanvasPopupStatus;
import com.interplug.qcast.biz.estimate.dto.*;
+
+import com.interplug.qcast.biz.pwrGnrSimulation.dto.PwrGnrSimRoofResponse;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.usermodel.DataFormat;
+import org.apache.poi.ss.usermodel.Drawing;
+import org.apache.poi.ss.usermodel.PictureData;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.usermodel.WorkbookFactory;
+import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
+import org.apache.poi.xssf.usermodel.XSSFDrawing;
+import org.apache.poi.xssf.usermodel.XSSFPicture;
+import org.apache.poi.xssf.usermodel.XSSFShape;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.apache.poi.util.Units;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpMethod;
+import org.springframework.stereotype.Service;
+import org.springframework.web.util.HtmlUtils;
+import org.springframework.web.util.UriComponentsBuilder;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.interplug.qcast.biz.canvasStatus.CanvasStatusService;
+import com.interplug.qcast.biz.canvasStatus.dto.CanvasStatusCopyRequest;
import com.interplug.qcast.biz.file.FileMapper;
import com.interplug.qcast.biz.file.dto.FileRequest;
import com.interplug.qcast.biz.file.dto.FileResponse;
import com.interplug.qcast.biz.object.ObjectMapper;
+import com.interplug.qcast.biz.object.dto.ObjectRequest;
import com.interplug.qcast.biz.object.dto.ObjectResponse;
-import com.interplug.qcast.biz.object.dto.PlanRequest;
-import com.interplug.qcast.biz.object.dto.PlanResponse;
+import com.interplug.qcast.biz.pwrGnrSimulation.PwrGnrSimService;
+import com.interplug.qcast.biz.pwrGnrSimulation.dto.PwrGnrSimGuideResponse;
+import com.interplug.qcast.biz.pwrGnrSimulation.dto.PwrGnrSimRequest;
+import com.interplug.qcast.biz.pwrGnrSimulation.dto.PwrGnrSimResponse;
import com.interplug.qcast.config.Exception.ErrorCode;
import com.interplug.qcast.config.Exception.QcastException;
import com.interplug.qcast.config.message.Messages;
import com.interplug.qcast.util.ExcelUtil;
import com.interplug.qcast.util.InterfaceQsp;
-import io.micrometer.common.util.StringUtils;
+import com.interplug.qcast.util.PdfUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
-import java.beans.BeanInfo;
-import java.beans.Introspector;
-import java.beans.PropertyDescriptor;
-import java.io.File;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.math.BigDecimal;
-import java.util.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.http.HttpMethod;
-import org.springframework.stereotype.Service;
-import org.springframework.web.util.UriComponentsBuilder;
@Slf4j
@Service
@@ -39,13 +76,20 @@ import org.springframework.web.util.UriComponentsBuilder;
public class EstimateService {
private final InterfaceQsp interfaceQsp;
- @Autowired Messages message;
+ @Autowired
+ Messages message;
+
+ @Value("${file.ini.root.path}")
+ private String baseDirPath;
+
+ @Value("${file.ini.drawing.img.path}")
+ private String drawingDirPath;
@Value("${qsp.url}")
private String QSP_API_URL;
- @Value("${file.excel.template.path}")
- private String excelTemplateFilePath;
+ @Value("${front.url}")
+ private String frontUrl;
private final ObjectMapper objectMapper;
@@ -53,50 +97,53 @@ public class EstimateService {
private final FileMapper fileMapper;
+ @Autowired
+ private CanvasStatusService canvasStatusService;
+
+ @Autowired
+ private PwrGnrSimService pwrGnrSimService;
+
+ @Autowired
+ private CanvasPopupStatusService canvasPopupStatusService;
+
/**
- * 1차점 price 관리 목록 조회
+ * QSP 1차점 price 관리 목록 조회
*
* @param priceRequest 1차점 price 관련 정보
* @return PriceResponse 1차점 price 목록
* @throws Exception
*/
- public PriceResponse selectStorePriceList(PriceRequest priceRequest) throws Exception {
+ public EstimateApiResponse selectStorePriceList(PriceRequest priceRequest) throws Exception {
// Validation
if (StringUtils.isEmpty(priceRequest.getSaleStoreId())) {
- throw new QcastException(
- ErrorCode.INVALID_INPUT_VALUE,
- message.getMessage("common.message.required.data", "Sale Store ID"));
+ throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
+ message.getMessage("common.message.required.data", "Sale Store ID"));
}
if (StringUtils.isEmpty(priceRequest.getSapSalesStoreCd())) {
- throw new QcastException(
- ErrorCode.INVALID_INPUT_VALUE,
- message.getMessage("common.message.required.data", "Sap Sale Store Code"));
+ throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
+ message.getMessage("common.message.required.data", "Sap Sale Store Code"));
}
if (StringUtils.isEmpty(priceRequest.getDocTpCd())) {
- throw new QcastException(
- ErrorCode.INVALID_INPUT_VALUE,
- message.getMessage("common.message.required.data", "Estimate Type"));
+ throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
+ message.getMessage("common.message.required.data", "Estimate Type"));
}
- PriceResponse response = null;
+ EstimateApiResponse response = null;
/* [1]. QSP API (url + param) Setting */
String url = QSP_API_URL + "/api/price/storePriceList";
- String apiUrl =
- UriComponentsBuilder.fromHttpUrl(url)
+ String apiUrl = UriComponentsBuilder.fromHttpUrl(url)
.queryParam("saleStoreId", priceRequest.getSaleStoreId())
.queryParam("sapSalesStoreCd", priceRequest.getSapSalesStoreCd())
- .queryParam("docTpCd", priceRequest.getDocTpCd())
- .build()
- .toUriString();
+ .queryParam("docTpCd", priceRequest.getDocTpCd()).build().toUriString();
/* [2]. QSP API CALL -> Response */
String strResponse = interfaceQsp.callApi(HttpMethod.GET, apiUrl, null);
if (!"".equals(strResponse)) {
com.fasterxml.jackson.databind.ObjectMapper om =
- new com.fasterxml.jackson.databind.ObjectMapper()
- .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
- response = om.readValue(strResponse, PriceResponse.class);
+ new com.fasterxml.jackson.databind.ObjectMapper()
+ .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ response = om.readValue(strResponse, EstimateApiResponse.class);
} else {
// [msg] No data
throw new QcastException(ErrorCode.NOT_FOUND, message.getMessage("common.message.no.data"));
@@ -106,39 +153,37 @@ public class EstimateService {
}
/**
- * @param priceRequest
- * @return PriceResponse
+ * QSP 아이템 가격 목록 조회
+ *
+ * @param priceRequest QSP 관련 아이템 목록 정보
+ * @return PriceResponse QSP 아이템 가격 목록
* @throws Exception
*/
- public PriceResponse selectItemPriceList(PriceRequest priceRequest) throws Exception {
+ public EstimateApiResponse selectItemPriceList(PriceRequest priceRequest) throws Exception {
// Validation
if (StringUtils.isEmpty(priceRequest.getSaleStoreId())) {
- throw new QcastException(
- ErrorCode.INVALID_INPUT_VALUE,
- message.getMessage("common.message.required.data", "Sale Store ID"));
+ throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
+ message.getMessage("common.message.required.data", "Sale Store ID"));
}
if (StringUtils.isEmpty(priceRequest.getSapSalesStoreCd())) {
- throw new QcastException(
- ErrorCode.INVALID_INPUT_VALUE,
- message.getMessage("common.message.required.data", "Sap Sale Store Code"));
+ throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
+ message.getMessage("common.message.required.data", "Sap Sale Store Code"));
}
if (StringUtils.isEmpty(priceRequest.getPriceCd())) {
- throw new QcastException(
- ErrorCode.INVALID_INPUT_VALUE,
- message.getMessage("common.message.required.data", "Price Code"));
+ throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
+ message.getMessage("common.message.required.data", "Price Code"));
}
- PriceResponse response = null;
+ EstimateApiResponse response = null;
/* [1]. QSP API CALL -> Response */
- String strResponse =
- interfaceQsp.callApi(
- HttpMethod.POST, QSP_API_URL + "/api//price/storePriceItemList", priceRequest);
+ String strResponse = interfaceQsp.callApi(HttpMethod.POST,
+ QSP_API_URL + "/api//price/storePriceItemList", priceRequest);
if (!"".equals(strResponse)) {
com.fasterxml.jackson.databind.ObjectMapper om =
- new com.fasterxml.jackson.databind.ObjectMapper()
- .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
- response = om.readValue(strResponse, PriceResponse.class);
+ new com.fasterxml.jackson.databind.ObjectMapper()
+ .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ response = om.readValue(strResponse, EstimateApiResponse.class);
} else {
// [msg] No data
throw new QcastException(ErrorCode.NOT_FOUND, message.getMessage("common.message.no.data"));
@@ -147,23 +192,48 @@ public class EstimateService {
return response;
}
+ /**
+ * 견적 특이사항 타이틀 목록 조회
+ *
+ * @param noteRequest 견적 특이사항 조회 정보
+ * @return List 견적 특이사항 목록
+ * @throws Exception
+ */
+ public List selectSpecialNoteTitleList(NoteRequest noteRequest) throws Exception {
+ return estimateMapper.selectEstimateNoteTitleList(noteRequest);
+ }
+
+ /**
+ * 견적 특이사항 목록 조회
+ *
+ * @param noteRequest 견적 특이사항 조회 정보
+ * @return List 견적 특이사항 목록
+ * @throws Exception
+ */
public List selectSpecialNoteList(NoteRequest noteRequest) throws Exception {
return estimateMapper.selectEstimateNoteList(noteRequest);
}
+ /**
+ * 견적서 상세 조회
+ *
+ * @param objectNo 물건번호
+ * @param planNo 플랜번호
+ * @return EstimateResponse 견적서 상세 정보
+ * @throws Exception
+ */
public EstimateResponse selectEstimateDetail(String objectNo, String planNo) throws Exception {
// Validation
if (StringUtils.isEmpty(objectNo)) {
- throw new QcastException(
- ErrorCode.INVALID_INPUT_VALUE,
- message.getMessage("common.message.required.data", "Object No"));
+ throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
+ message.getMessage("common.message.required.data", "Object No"));
}
if (StringUtils.isEmpty(planNo)) {
- throw new QcastException(
- ErrorCode.INVALID_INPUT_VALUE,
- message.getMessage("common.message.required.data", "Plan No"));
+ throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
+ message.getMessage("common.message.required.data", "Plan No"));
}
+ String splitStr = "、";
EstimateRequest estimateRequest = new EstimateRequest();
estimateRequest.setObjectNo(objectNo);
estimateRequest.setPlanNo(planNo);
@@ -175,6 +245,45 @@ public class EstimateService {
throw new QcastException(ErrorCode.NOT_FOUND, message.getMessage("common.message.no.data"));
} else {
+ // Plan에서 지붕재, 시공방법 동일 데이타 중복 제거
+ String roofCheckDatas = "";
+ String roofMaterialIdMultis = "";
+ String constructSpecificationMultis = "";
+ String orgRoofMaterialIdMultis = response.getRoofMaterialIdMulti();
+ String orgConstructSpecificationMultis = response.getConstructSpecificationMulti();
+
+ if (!StringUtils.isEmpty(orgRoofMaterialIdMultis)
+ && !StringUtils.isEmpty(orgConstructSpecificationMultis)) {
+ String[] arrOrgRoofMaterialIdMultis = orgRoofMaterialIdMultis.split(splitStr);
+ String[] arrOrgConstructSpecificationMultis =
+ orgConstructSpecificationMultis.split(splitStr);
+
+ if (arrOrgRoofMaterialIdMultis.length == arrOrgConstructSpecificationMultis.length) {
+ for (int i = 0; i < arrOrgRoofMaterialIdMultis.length; i++) {
+
+ if (!roofCheckDatas.contains(
+ arrOrgRoofMaterialIdMultis[i] + "_" + arrOrgConstructSpecificationMultis[i])) {
+ roofMaterialIdMultis +=
+ StringUtils.isEmpty(roofMaterialIdMultis) ? arrOrgRoofMaterialIdMultis[i]
+ : splitStr + arrOrgRoofMaterialIdMultis[i];
+ constructSpecificationMultis += StringUtils.isEmpty(constructSpecificationMultis)
+ ? arrOrgConstructSpecificationMultis[i]
+ : splitStr + arrOrgConstructSpecificationMultis[i];
+ }
+
+ roofCheckDatas +=
+ arrOrgRoofMaterialIdMultis[i] + "_" + arrOrgConstructSpecificationMultis[i];
+ }
+
+ if (!StringUtils.isEmpty(roofMaterialIdMultis)) {
+ response.setRoofMaterialIdMulti(roofMaterialIdMultis);
+ }
+ if (!StringUtils.isEmpty(roofMaterialIdMultis)) {
+ response.setConstructSpecificationMulti(constructSpecificationMultis);
+ }
+ }
+ }
+
// 아이템 목록 조회
List estimateItemList = estimateMapper.selectEstimateItemList(estimateRequest);
response.setItemList(estimateItemList);
@@ -195,51 +304,81 @@ public class EstimateService {
return response;
}
+ /**
+ * 견적서 저장
+ *
+ * @param estimateRequest 견적서 저장 정보
+ * @throws Exception
+ */
public void insertEstimate(EstimateRequest estimateRequest) throws Exception {
// Validation
if (StringUtils.isEmpty(estimateRequest.getObjectNo())) {
- throw new QcastException(
- ErrorCode.INVALID_INPUT_VALUE,
- message.getMessage("common.message.required.data", "Object No"));
+ throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
+ message.getMessage("common.message.required.data", "Object No"));
}
if (StringUtils.isEmpty(estimateRequest.getPlanNo())) {
- throw new QcastException(
- ErrorCode.INVALID_INPUT_VALUE,
- message.getMessage("common.message.required.data", "Plan No"));
+ throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
+ message.getMessage("common.message.required.data", "Plan No"));
}
if (StringUtils.isEmpty(estimateRequest.getSaleStoreId())) {
- throw new QcastException(
- ErrorCode.INVALID_INPUT_VALUE,
- message.getMessage("common.message.required.data", "Sale Store ID"));
+ throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
+ message.getMessage("common.message.required.data", "Sale Store ID"));
}
- if (StringUtils.isEmpty(estimateRequest.getSapSalesStoreCd())) {
- throw new QcastException(
- ErrorCode.INVALID_INPUT_VALUE,
- message.getMessage("common.message.required.data", "Sap Sale Store Code"));
+ if (StringUtils.isEmpty(estimateRequest.getUserId())) {
+ throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
+ message.getMessage("common.message.required.data", "User ID"));
+ }
+
+ List circuitItemList = estimateRequest.getCircuitItemList(); //drawingFlg != "1" 인 경우 (도면 없이 저장) circuitItemList는 비어있어도 정상
+ List itemList = estimateRequest.getItemList();
+
+ if (itemList == null || itemList.isEmpty()) {
+ try {
+ Map errReq = new HashMap<>();
+ errReq.put("objectNo", estimateRequest.getObjectNo());
+ errReq.put("planNo", estimateRequest.getPlanNo());
+ errReq.put("rejectRsn", "itemList is empty");
+ errReq.put("regId", estimateRequest.getUserId());
+ interfaceQsp.callApi(HttpMethod.POST, QSP_API_URL + "/api/master/quotationErrSave", errReq);
+ } catch (Exception e) {
+ log.warn("quotationErrSave failed: {}", e.getMessage());
+ }
+ throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
+ message.getMessage("common.message.required.data", "itemList"));
}
String splitStr = "、";
+ List orgRoofList = new ArrayList();
List roofList = new ArrayList();
- List itemList = estimateRequest.getItemList();
+
+ estimateRequest.setTempFlg("0");
try {
// 도면 작성일 경우에만 지붕재 데이터를 셋팅
if ("1".equals(estimateRequest.getDrawingFlg())) {
// [1]. 견적서 기본셋팅
+ // 플랜정보 조회 (임시저장여부 가져오기)
+ EstimateResponse estimateResponse = estimateMapper.selectEstimateDetail(estimateRequest);
estimateRequest.setEstimateType("YJOD");
estimateRequest.setPriceCd("UNIT_PRICE");
+ estimateRequest.setTempFlg("0".equals(estimateResponse.getTempFlg()) ? "0" : "1");
// 물건정보 조회 후 데이터 축출
ObjectResponse objectResponse =
- objectMapper.selectObjectDetail(estimateRequest.getObjectNo());
+ objectMapper.selectObjectDetail(estimateRequest.getObjectNo());
if (objectResponse != null) {
- estimateRequest.setWeatherPoint(
- objectResponse.getPrefName() + " - " + objectResponse.getAreaName());
+ estimateRequest
+ .setWeatherPoint(objectResponse.getPrefName() + " - " + objectResponse.getAreaName());
estimateRequest.setCharger(objectResponse.getReceiveUser());
}
// [2]. 지붕재 관련 데이터 셋팅
- roofList = estimateRequest.getRoofList();
+ orgRoofList = estimateRequest.getRoofSurfaceList();
+ for (RoofRequest roofRequest : orgRoofList) {
+ if (!roofRequest.getModuleList().isEmpty()) { // 빈 배열은 지붕재 데이타에서 제거
+ roofList.add(roofRequest);
+ }
+ }
// 지붕재 시공사양 ID
String constructSpecifications = "";
@@ -268,7 +407,7 @@ public class EstimateService {
if (!StringUtils.isEmpty(roofRequest.getConstructSpecification())) {
constructSpecifications +=
- !StringUtils.isEmpty(constructSpecifications) ? splitStr : "";
+ !StringUtils.isEmpty(constructSpecifications) ? splitStr : "";
constructSpecifications += roofRequest.getConstructSpecification();
}
@@ -284,7 +423,7 @@ public class EstimateService {
if (!StringUtils.isEmpty(roofRequest.getConstructSpecificationMulti())) {
constructSpecificationMultis +=
- !StringUtils.isEmpty(constructSpecificationMultis) ? splitStr : "";
+ !StringUtils.isEmpty(constructSpecificationMultis) ? splitStr : "";
constructSpecificationMultis += roofRequest.getConstructSpecificationMulti();
}
@@ -312,10 +451,13 @@ public class EstimateService {
estimateRequest.setArrItemId(arrItemId);
// 아이템의 마스터 정보 및 정가 정보 조회
List itemResponseList = estimateMapper.selectItemMasterList(estimateRequest);
+ // BOM 정보 목록
+ List estimateBomList = new ArrayList();
int j = 1;
for (ItemRequest itemRequest : itemList) {
- itemRequest.setDispOrder(String.valueOf(j++));
+ int dispOrder = 100 * j++;
+ itemRequest.setDispOrder(String.valueOf(dispOrder));
for (ItemResponse itemResponse : itemResponseList) {
if (itemRequest.getItemId().equals(itemResponse.getItemId())) {
@@ -328,12 +470,58 @@ public class EstimateService {
itemRequest.setSalePrice(itemResponse.getSalePrice());
itemRequest.setFileUploadFlg(itemResponse.getFileUploadFlg());
itemRequest.setPkgMaterialFlg(itemResponse.getPkgMaterialFlg());
+ itemRequest.setOpenFlg(itemResponse.getOpenFlg());
+ itemRequest.setUnitOpenFlg(itemResponse.getUnitOpenFlg());
+ itemRequest.setDispCableFlg("CABLE_".equals(itemResponse.getItemGroup()) ? "1" : "0");
itemRequest.setItemGroup(itemResponse.getItemGroup());
+ itemRequest.setItemCtgGr(itemResponse.getItemCtgGr());
+ itemRequest.setPartAdd("0");
+ itemRequest.setDelFlg("0");
break;
}
}
+ itemRequest.setNorthModuleYn(itemRequest.getNorthModuleYn());
+ // 아이템 BOM Header인 경우 컴포넌트 등록 처리
+ if ("ERLA".equals(itemRequest.getItemCtgGr())) {
+ List itemBomList =
+ estimateMapper.selectItemMasterBomList(itemRequest.getItemId());
+
+ int k = 1;
+ for (ItemResponse itemResponse : itemBomList) {
+ ItemRequest bomItem = new ItemRequest();
+
+ bomItem.setPaDispOrder(String.valueOf(dispOrder));
+ bomItem.setDispOrder(String.valueOf(dispOrder + k++));
+ bomItem.setItemId(itemResponse.getItemId());
+ bomItem.setItemNo(itemResponse.getItemNo());
+ bomItem.setItemName(itemResponse.getItemName());
+ bomItem.setUnit(itemResponse.getUnit());
+ bomItem.setPnowW(itemResponse.getPnowW());
+ bomItem.setSpecification(itemResponse.getPnowW());
+ bomItem.setAmount(String.valueOf(Integer.parseInt(itemResponse.getBomAmount())
+ * Integer.parseInt(itemRequest.getAmount())));
+ bomItem.setBomAmount(itemResponse.getBomAmount());
+ bomItem.setUnitPrice(itemResponse.getSalePrice());
+ bomItem.setSalePrice(itemResponse.getSalePrice());
+ bomItem.setFileUploadFlg(itemResponse.getFileUploadFlg());
+ bomItem.setPkgMaterialFlg(itemResponse.getPkgMaterialFlg());
+ bomItem.setOpenFlg("0");
+ bomItem.setUnitOpenFlg("0");
+ bomItem.setDispCableFlg("0");
+ bomItem.setItemGroup(itemResponse.getItemGroup());
+ bomItem.setItemCtgGr(itemResponse.getItemCtgGr());
+ bomItem.setPartAdd("0");
+ bomItem.setDelFlg("0");
+
+ estimateBomList.add(bomItem);
+ }
+ }
}
+ System.out.print("itemRequest");
+ // BOM 컴포넌트 추가
+ itemList.addAll(estimateBomList);
+
// [4]. 견적특이사항 관련 데이터 셋팅
NoteRequest noteRequest = new NoteRequest();
noteRequest.setArrItemId(arrItemId);
@@ -355,15 +543,16 @@ public class EstimateService {
estimateOptions += !StringUtils.isEmpty(estimateOptions) ? splitStr : "";
estimateOptions += "ATTR003"; // 출력제어시간 기본 체크
} else if ("ATTR004".equals(noteResponse.getCode())
- && "1".equals(estimateRequest.getNorthArrangement())) {
+ && "1".equals(estimateRequest.getNorthArrangement())) {
estimateOptions += !StringUtils.isEmpty(estimateOptions) ? splitStr : "";
estimateOptions += "ATTR004"; // 북면설치 체크
} else if ("ATTR005".equals(noteResponse.getCode())
- && "1".equals(objectResponse.getSaltAreaFlg())) {
+ && "1".equals(objectResponse != null ? objectResponse.getSaltAreaFlg() : "")) {
estimateOptions += !StringUtils.isEmpty(estimateOptions) ? splitStr : "";
estimateOptions += "ATTR005"; // 염해지역 체크
- } else if ("ATTR006".equals(objectResponse.getColdRegionFlg())
- && "1".equals(estimateRequest.getNorthArrangement())) {
+ } else if ("ATTR006"
+ .equals(objectResponse != null ? objectResponse.getColdRegionFlg() : "")
+ && "1".equals(estimateRequest.getNorthArrangement())) {
estimateOptions += !StringUtils.isEmpty(estimateOptions) ? splitStr : "";
estimateOptions += "ATTR006"; // 적설지역 체크
} else if ("ATTR007".equals(noteResponse.getCode())) {
@@ -396,6 +585,13 @@ public class EstimateService {
}
itemRequest.setSpecialNoteCd(specialNoteCd);
}
+
+ // 첨부파일 초기화
+ FileRequest fileRequest = new FileRequest();
+ fileRequest.setObjectNo(estimateRequest.getObjectNo());
+ fileRequest.setPlanNo(estimateRequest.getPlanNo());
+ fileRequest.setUserId(estimateRequest.getUserId());
+ fileMapper.deleteFile(fileRequest);
}
// 아이템 목록 필수 값 체크
@@ -403,24 +599,622 @@ public class EstimateService {
String moduleModel = "";
String pcTypeNo = "";
for (ItemRequest itemRequest : itemList) {
+ if (!"1".equals(itemRequest.getDelFlg())) {
+ if (StringUtils.isEmpty(itemRequest.getDispOrder())) {
+ throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
+ message.getMessage("common.message.required.data", "Display Order"));
+ }
+ if (StringUtils.isEmpty(itemRequest.getItemId())) {
+ throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
+ message.getMessage("common.message.required.data", "Item ID"));
+ }
+ if (StringUtils.isEmpty(itemRequest.getAmount())) {
+ throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
+ message.getMessage("common.message.required.data", "Item Amount"));
+ }
+
+ // 수량
+ BigDecimal amount = new BigDecimal(
+ StringUtils.isEmpty(itemRequest.getAmount()) ? "0" : itemRequest.getAmount());
+ // 아이템용량
+ BigDecimal pnowW = new BigDecimal(
+ StringUtils.isEmpty(itemRequest.getPnowW()) ? "0" : itemRequest.getPnowW());
+
+ // 모듈/PC 체크
+ if ("MODULE_".equals(itemRequest.getItemGroup())) {
+ moduleModel += !StringUtils.isEmpty(moduleModel) ? splitStr : "";
+ moduleModel += itemRequest.getItemNo();
+
+ capacity = capacity.add(amount.multiply(pnowW));
+ } else if ("PC_".equals(itemRequest.getItemGroup())) {
+ pcTypeNo += !StringUtils.isEmpty(pcTypeNo) ? splitStr : "";
+ pcTypeNo += itemRequest.getItemNo();
+ } else if ("STORAGE_BATTERY".equals(itemRequest.getItemGroup())) {
+ pcTypeNo += !StringUtils.isEmpty(pcTypeNo) ? splitStr : "";
+ pcTypeNo += itemRequest.getItemNo();
+ }
+ }
+ }
+ estimateRequest.setCapacity(String.valueOf(capacity));
+ estimateRequest.setModuleModel(moduleModel);
+ estimateRequest.setPcTypeNo(pcTypeNo);
+
+ // 물건정보 수정
+ if (!StringUtils.isEmpty(estimateRequest.getObjectName())
+ || !StringUtils.isEmpty(estimateRequest.getObjectNameOmit())) {
+ estimateMapper.updateObject(estimateRequest);
+ }
+ if (!StringUtils.isEmpty(estimateRequest.getSurfaceType())
+ || !StringUtils.isEmpty(estimateRequest.getSetupHeight())
+ || !StringUtils.isEmpty(estimateRequest.getStandardWindSpeedId())
+ || !StringUtils.isEmpty(estimateRequest.getSnowfall())) {
+ estimateMapper.updateObjectInfo(estimateRequest);
+ }
+
+ // 견적서 정보 수정
+ estimateRequest.setPriceCd(
+ !StringUtils.isEmpty(estimateRequest.getPriceCd()) ? estimateRequest.getPriceCd()
+ : "UNIT_PRICE");
+ estimateMapper.updateEstimate(estimateRequest);
+ estimateMapper.updateEstimateInfo(estimateRequest);
+
+ // 도면 작성일 경우에만 지붕면, 도면 아이템 데이터 초기화 후 저장
+ if ("1".equals(estimateRequest.getDrawingFlg())) {
+ // 견적서 지붕면/아이템 및 PC 회로구성도 제거
+ estimateMapper.deleteEstimateRoofList(estimateRequest);
+ estimateMapper.deleteEstimateRoofItemList(estimateRequest);
+ estimateMapper.deleteEstimateCircuitItemList(estimateRequest);
+
+ // 견적서 지붕면/아이템 신규 추가
+ List allModuleList = new ArrayList();
+ for (RoofRequest roofRequest : roofList) {
+ roofRequest.setObjectNo(estimateRequest.getObjectNo());
+ roofRequest.setPlanNo(estimateRequest.getPlanNo());
+ roofRequest.setUserId(estimateRequest.getUserId());
+
+ estimateMapper.insertEstimateRoof(roofRequest);
+
+ List moduleList = roofRequest.getModuleList();
+ List roofItemList = new ArrayList();
+
+ // 동일 모듈, PCS 아이템 묶기
+ for (ItemRequest itemRequest : moduleList) {
+ boolean overLap = false;
+ for (ItemRequest data : roofItemList) {
+ if (itemRequest.getItemId().equals(data.getItemId())
+ && itemRequest.getPcItemId().equals(data.getPcItemId())) {
+ data.setAmount(String.valueOf(Integer.parseInt(data.getAmount()) + 1)); // 데이터 존재하면
+ // 카운팅 + 1
+ overLap = true;
+ break;
+ }
+ }
+
+ if (!overLap) {
+ itemRequest.setAmount("1");
+ roofItemList.add(itemRequest);
+ }
+
+ allModuleList.add(itemRequest);
+ }
+
+ for (ItemRequest itemRequest : roofItemList) {
+ itemRequest.setRoofSurfaceId(roofRequest.getRoofSurfaceId());
+ itemRequest.setObjectNo(estimateRequest.getObjectNo());
+ itemRequest.setPlanNo(estimateRequest.getPlanNo());
+ // 선택한 PCS 아이템이 다른 경우
+ for (ItemRequest itemObj : itemList) {
+ if(itemObj.getQcastCustPrdId() != null && itemObj.getQcastCustPrdId().equals(itemRequest.getPcItemId())) {
+ itemRequest.setQcastCustPrdId(itemObj.getItemId());
+ }
+ if(itemObj.getItemId().equals(itemRequest.getItemId())) {
+ itemRequest.setNorthModuleYn(itemObj.getNorthModuleYn());
+ }
+
+ }
+ estimateMapper.insertEstimateRoofItem(itemRequest);
+ }
+ }
+
+ // 견적서 PCS 아이템 회로 구성
+ List circuitPcsItemList =
+ this.getPcsCircuitList(circuitItemList, allModuleList);
+
+ // 견적서 회로구성 아이템 신규 추가
+ for (ItemRequest circuitItemRequest : circuitPcsItemList) {
+ circuitItemRequest.setObjectNo(estimateRequest.getObjectNo());
+ circuitItemRequest.setPlanNo(estimateRequest.getPlanNo());
+
+ if (!StringUtils.isEmpty(circuitItemRequest.getCircuitCfg())) {
+ estimateMapper.insertEstimateCircuitItem(circuitItemRequest);
+ }
+ }
+
+ // 견적서 도면 아이템 제거
+ estimateMapper.deleteEstimateDrawingItemList(estimateRequest);
+ // 견적서 도면 아이템 등록
+ for (ItemRequest itemRequest : itemList) {
+ itemRequest.setObjectNo(estimateRequest.getObjectNo());
+ itemRequest.setPlanNo(estimateRequest.getPlanNo());
+ itemRequest.setAmount(
+ !StringUtils.isEmpty(itemRequest.getAmount()) ? itemRequest.getAmount() : "0");
+ itemRequest.setUserId(estimateRequest.getUserId());
+
+ // BOM 컴포넌트는 제외하고 등록
+ if (StringUtils.isEmpty(itemRequest.getPaDispOrder())) {
+ estimateMapper.insertEstimateDrawingItem(itemRequest);
+ }
+ }
+ }
+
+ // 견적서 모든 아이템 제거
+ estimateMapper.deleteEstimateItemList(estimateRequest);
+ estimateMapper.deleteEstimateInfoItemList(estimateRequest);
+
+ // 견적서 아이템 신규 추가
+ String hisNo = estimateMapper.selectEstimateItemHisNo(estimateRequest); // 아이템 히스토리 번호 조회
+ for (ItemRequest itemRequest : itemList) {
+ itemRequest.setHisNo(hisNo);
+ itemRequest.setObjectNo(estimateRequest.getObjectNo());
+ itemRequest.setPlanNo(estimateRequest.getPlanNo());
+ itemRequest.setAmount(
+ !StringUtils.isEmpty(itemRequest.getAmount()) ? itemRequest.getAmount() : "0");
+ itemRequest.setSalePrice(
+ !StringUtils.isEmpty(itemRequest.getSalePrice()) ? itemRequest.getSalePrice() : "0");
+ itemRequest.setBomAmount(
+ !StringUtils.isEmpty(itemRequest.getBomAmount()) ? itemRequest.getBomAmount() : "0");
+ itemRequest.setPartAdd(
+ !StringUtils.isEmpty(itemRequest.getPartAdd()) ? itemRequest.getPartAdd() : "0");
+ itemRequest.setOpenFlg(
+ !StringUtils.isEmpty(itemRequest.getOpenFlg()) ? itemRequest.getOpenFlg() : "0");
+ itemRequest.setUnitOpenFlg(
+ !StringUtils.isEmpty(itemRequest.getUnitOpenFlg()) ? itemRequest.getUnitOpenFlg() : "0");
+ itemRequest.setItemChangeFlg(
+ !StringUtils.isEmpty(itemRequest.getItemChangeFlg()) ? itemRequest.getItemChangeFlg()
+ : "0");
+ itemRequest.setDispCableFlg(
+ !StringUtils.isEmpty(itemRequest.getDispCableFlg()) ? itemRequest.getDispCableFlg()
+ : "0");
+ itemRequest.setUserId(estimateRequest.getUserId());
+
+ estimateMapper.insertEstimateItemHis(itemRequest);
+
+ if (!"1".equals(itemRequest.getDelFlg())) {
+ estimateMapper.insertEstimateItem(itemRequest);
+ estimateMapper.insertEstimateInfoItem(itemRequest);
+ }
+ }
+
+ // 첨부파일 처리
+ if (estimateRequest.getFileList() != null) {
+ for (FileRequest fileRequest : estimateRequest.getFileList()) {
+ fileRequest.setUserId(estimateRequest.getUserId());
+ fileMapper.insertFile(fileRequest);
+ fileMapper.insertFileInfo(fileRequest);
+ }
+ }
+
+ if (estimateRequest.getDeleteFileList() != null) {
+ for (FileRequest fileRequest : estimateRequest.getDeleteFileList()) {
+ fileRequest.setUserId(estimateRequest.getUserId());
+ fileMapper.deleteFile(fileRequest);
+ }
+ }
+
+ // 임시저장 상태에서는 인터페이스 막도록 처리
+ if ("0".equals(estimateRequest.getTempFlg())) {
+ // QSP Q.CAST SEND API
+ List resultList = new ArrayList();
+ resultList = this.sendEstimateApi(estimateRequest);
+ // API에서 받은 문서번호 업데이트
+ for (EstimateSendResponse result : resultList) {
+ estimateRequest.setObjectNo(result.getObjectNo());
+ estimateRequest.setPlanNo(result.getPlanNo());
+ estimateRequest.setDocNo(result.getDocNo());
+ estimateRequest.setSyncFlg(result.getSyncFlg());
+
+ estimateMapper.updateEstimateApi(estimateRequest);
+ }
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new QcastException(ErrorCode.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ /**
+ * 견적서 복사
+ *
+ * @param estimateCopyRequest 견적서 복사 정보
+ * @return EstimateResponse 견적서 복사 후 상세 정보
+ * @throws Exception
+ */
+ public EstimateResponse insertEstimateCopy(EstimateCopyRequest estimateCopyRequest)
+ throws Exception {
+ // Validation
+ if (StringUtils.isEmpty(estimateCopyRequest.getObjectNo())) {
+ throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
+ message.getMessage("common.message.required.data", "Object No"));
+ }
+ if (StringUtils.isEmpty(estimateCopyRequest.getPlanNo())) {
+ throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
+ message.getMessage("common.message.required.data", "Plan No"));
+ }
+ if (StringUtils.isEmpty(estimateCopyRequest.getUserId())) {
+ throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
+ message.getMessage("common.message.required.data", "User ID"));
+ }
+
+ // 응답 객체
+ EstimateResponse response = new EstimateResponse();
+
+ try {
+
+ ObjectRequest objectRequest = new ObjectRequest();
+ objectRequest.setSaleStoreId(estimateCopyRequest.getCopySaleStoreId());
+ objectRequest
+ .setReceiveUser(StringUtils.defaultString(estimateCopyRequest.getCopyReceiveUser()));
+ objectRequest.setDelFlg("1");
+ objectRequest.setOrgDelFlg("0");
+ objectRequest.setTempFlg("0");
+ objectRequest.setTempDelFlg("0");
+ objectRequest.setUserId(estimateCopyRequest.getUserId());
+
+ // [1]. 신규 물건번호 생성
+ objectMapper.insertObjectNo(objectRequest);
+ objectRequest.setObjectNo(estimateCopyRequest.getObjectNo());
+ objectRequest.setCopyObjectNo(objectMapper.selectObjectNo(objectRequest));
+
+ // [2]. 물건정보 복사
+ objectRequest.setContentsPath(baseDirPath + "\\\\" + objectRequest.getCopyObjectNo());
+ objectMapper.insertObjectCopy(objectRequest);
+ objectMapper.insertObjectInfoCopy(objectRequest);
+ objectRequest.setObjectNo(objectRequest.getCopyObjectNo());
+ objectMapper.updateObjectDelivery(objectRequest);
+
+ // [3]. 아이템 관련 데이터 셋팅 (복사 시 정가 셋팅)
+ EstimateRequest estimateRequest = new EstimateRequest();
+ estimateRequest.setObjectNo(estimateCopyRequest.getObjectNo());
+ estimateRequest.setPlanNo(estimateCopyRequest.getPlanNo());
+
+ List itemList = new ArrayList();
+ List estimateItemList = estimateMapper.selectEstimateItemList(estimateRequest);
+ for (ItemResponse itemResponse : estimateItemList) {
+ ItemRequest itemRequest = new ItemRequest();
+ itemRequest.setDispOrder(itemResponse.getDispOrder());
+ itemRequest.setPaDispOrder(itemResponse.getPaDispOrder());
+ itemRequest.setItemId(itemResponse.getItemId());
+ itemRequest.setItemNo(itemResponse.getItemNo());
+ itemRequest.setItemName(itemResponse.getItemName());
+ itemRequest.setUnit(itemResponse.getUnit());
+ itemRequest.setAmount(itemResponse.getAmount());
+ itemRequest.setBomAmount(itemResponse.getBomAmount());
+ itemRequest.setSpecialNoteCd(itemResponse.getSpecialNoteCd());
+ itemRequest.setItemChangeFlg("0");
+ itemRequest.setDispCableFlg(itemResponse.getDispCableFlg());
+
+ itemList.add(itemRequest);
+ }
+
+ if (!itemList.isEmpty()) {
+ String[] arrItemId = new String[itemList.size()];
+ int i = 0;
+ for (ItemRequest itemRequest : itemList) {
+ arrItemId[i++] = itemRequest.getItemId();
+ }
+ estimateRequest.setArrItemId(arrItemId);
+ // 아이템의 마스터 정보 및 정가 정보 조회
+ List itemResponseList = estimateMapper.selectItemMasterList(estimateRequest);
+
+ for (ItemRequest itemRequest : itemList) {
+ for (ItemResponse itemResponse : itemResponseList) {
+ if (itemRequest.getItemId().equals(itemResponse.getItemId())) {
+ itemRequest.setItemNo(itemResponse.getItemNo());
+ itemRequest.setItemName(itemResponse.getItemName());
+ itemRequest.setUnit(itemResponse.getUnit());
+ itemRequest.setPnowW(itemResponse.getPnowW());
+ itemRequest.setSpecification(itemResponse.getPnowW());
+ itemRequest.setUnitPrice(itemResponse.getSalePrice());
+ itemRequest.setSalePrice(itemResponse.getSalePrice());
+ itemRequest.setPkgMaterialFlg(itemResponse.getPkgMaterialFlg());
+ itemRequest.setFileUploadFlg(itemResponse.getFileUploadFlg());
+ itemRequest.setItemGroup(itemResponse.getItemGroup());
+ itemRequest.setOpenFlg(itemResponse.getOpenFlg());
+ itemRequest.setUnitOpenFlg(itemResponse.getUnitOpenFlg());
+
+ break;
+ }
+ }
+ }
+ }
+
+ // [4]. 견적서 복사
+ estimateCopyRequest.setCopyObjectNo(objectRequest.getObjectNo());
+ estimateCopyRequest.setCopyPlanNo("1");
+ estimateCopyRequest.setCopyReceiveUser(objectRequest.getReceiveUser()); //담당자
+ estimateMapper.insertEstimateCopy(estimateCopyRequest);
+ estimateMapper.insertEstimateInfoCopy(estimateCopyRequest);
+ estimateMapper.insertCanvasPopupStatusCopy(estimateCopyRequest);
+
+ // [5]. 견적서 아이템 복사
+ for (ItemRequest itemRequest : itemList) {
+ itemRequest.setObjectNo(estimateCopyRequest.getCopyObjectNo());
+ itemRequest.setPlanNo(estimateCopyRequest.getCopyPlanNo());
+ itemRequest.setPartAdd(
+ !StringUtils.isEmpty(itemRequest.getPartAdd()) ? itemRequest.getPartAdd() : "0");
+ itemRequest.setItemChangeFlg(
+ !StringUtils.isEmpty(itemRequest.getItemChangeFlg()) ? itemRequest.getItemChangeFlg()
+ : "0");
+ itemRequest.setUserId(estimateCopyRequest.getUserId());
+
+ estimateMapper.insertEstimateItem(itemRequest);
+ estimateMapper.insertEstimateInfoItem(itemRequest);
+ }
+
+ // [6]. 견적서 지붕면 및 도면 초기 데이터 복사
+ // 견적서 지붕면 복사
+ estimateMapper.insertEstimateRoofCopy(estimateCopyRequest);
+ // 견적서 지붕면 아이템 복사
+ estimateMapper.insertEstimateRoofItemCopy(estimateCopyRequest);
+ // 견적서 지붕면 회로구성 아이템 복사
+ estimateMapper.insertEstimateCircuitItemCopy(estimateCopyRequest);
+ // 도면 초기 데이타 복사(초기화 위해 필요)
+ estimateMapper.insertEstimateDrawingItemCopy(estimateCopyRequest);
+
+ // [7]. 견적서 도면 복사
+ CanvasStatusCopyRequest cs = new CanvasStatusCopyRequest();
+ cs.setOriginObjectNo(estimateCopyRequest.getObjectNo());
+ cs.setOriginPlanNo(estimateCopyRequest.getPlanNo());
+ cs.setObjectNo(estimateCopyRequest.getCopyObjectNo());
+ cs.setPlanNo(estimateCopyRequest.getCopyPlanNo());
+ cs.setUserId(estimateCopyRequest.getUserId());
+ canvasStatusService.copyCanvasStatus(cs, true);
+
+ } catch (Exception e) {
+ throw new QcastException(ErrorCode.INTERNAL_SERVER_ERROR);
+ }
+
+ // [9]. 최종 생성 물건번호 리턴
+ response.setObjectNo(estimateCopyRequest.getCopyObjectNo());
+ response.setPlanNo(estimateCopyRequest.getCopyPlanNo());
+ return response;
+ }
+
+ /**
+ * 견적서 초기화
+ *
+ * @param estimateRequest
+ * @return EstimateResponse 견적서 초기화 후 상세 정보
+ * @throws Exception
+ */
+ public EstimateResponse updateEstimateReset(EstimateRequest estimateRequest) throws Exception {
+ // Validation
+ if (StringUtils.isEmpty(estimateRequest.getObjectNo())) {
+ throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
+ message.getMessage("common.message.required.data", "Object No"));
+ }
+ if (StringUtils.isEmpty(estimateRequest.getPlanNo())) {
+ throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
+ message.getMessage("common.message.required.data", "Plan No"));
+ }
+ if (StringUtils.isEmpty(estimateRequest.getUserId())) {
+ throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
+ message.getMessage("common.message.required.data", "User ID"));
+ }
+
+ // 견적서 상세 조회
+ EstimateResponse estimateResponse = estimateMapper.selectEstimateDetail(estimateRequest);
+ if (estimateResponse == null) {
+ throw new QcastException(ErrorCode.NOT_FOUND,
+ message.getMessage("common.message.required.data", "Estimate Info"));
+ }
+
+ // 응답 객체
+ EstimateResponse response = new EstimateResponse();
+ String splitStr = "、";
+
+ try {
+ // [1]. 견적서 기본셋팅
+ estimateRequest.setEstimateType("YJOD");
+ estimateRequest.setPriceCd("UNIT_PRICE");
+ estimateRequest.setTempFlg("0".equals(estimateResponse.getTempFlg()) ? "0" : "1");
+
+ // 물건정보 조회 후 데이터 축출
+ ObjectResponse objectResponse =
+ objectMapper.selectObjectDetail(estimateRequest.getObjectNo());
+ if (objectResponse != null) {
+ estimateRequest
+ .setWeatherPoint(objectResponse.getPrefName() + " - " + objectResponse.getAreaName());
+ estimateRequest.setCharger(objectResponse.getReceiveUser());
+ }
+
+ // [2] 도면에서 저장된 아이템 목록 조회
+ List estimateItemList =
+ estimateMapper.selectEstimateDrawingItemList(estimateRequest);
+ List itemList = new ArrayList();
+ for (ItemResponse itemResponse : estimateItemList) {
+ ItemRequest itemRequest = new ItemRequest();
+ itemRequest.setObjectNo(itemResponse.getObjectNo());
+ itemRequest.setPlanNo(itemResponse.getPlanNo());
+ itemRequest.setItemId(itemResponse.getItemId());
+ itemRequest.setAmount(itemResponse.getAmount());
+
+ itemList.add(itemRequest);
+ }
+
+ // [3]. 아이템 관련 데이터 셋팅
+ String[] arrItemId = new String[itemList.size()];
+ int i = 0;
+ for (ItemRequest itemRequest : itemList) {
+ arrItemId[i++] = itemRequest.getItemId();
+ }
+ estimateRequest.setArrItemId(arrItemId);
+ // 아이템의 마스터 정보 및 정가 정보 조회
+ List itemResponseList = estimateMapper.selectItemMasterList(estimateRequest);
+ // BOM 정보 목록
+ List estimateBomList = new ArrayList();
+
+ int j = 1;
+ for (ItemRequest itemRequest : itemList) {
+ int dispOrder = 100 * j++;
+ itemRequest.setDispOrder(String.valueOf(dispOrder));
+
+ for (ItemResponse itemResponse : itemResponseList) {
+ if (itemRequest.getItemId().equals(itemResponse.getItemId())) {
+ itemRequest.setItemNo(itemResponse.getItemNo());
+ itemRequest.setItemName(itemResponse.getItemName());
+ itemRequest.setUnit(itemResponse.getUnit());
+ itemRequest.setPnowW(itemResponse.getPnowW());
+ itemRequest.setSpecification(itemResponse.getPnowW());
+ itemRequest.setUnitPrice(itemResponse.getSalePrice());
+ itemRequest.setSalePrice(itemResponse.getSalePrice());
+ itemRequest.setFileUploadFlg(itemResponse.getFileUploadFlg());
+ itemRequest.setPkgMaterialFlg(itemResponse.getPkgMaterialFlg());
+ itemRequest.setOpenFlg(itemResponse.getOpenFlg());
+ itemRequest.setUnitOpenFlg(itemResponse.getUnitOpenFlg());
+ itemRequest.setDispCableFlg("CABLE_".equals(itemResponse.getItemGroup()) ? "1" : "0");
+ itemRequest.setItemGroup(itemResponse.getItemGroup());
+ itemRequest.setItemCtgGr(itemResponse.getItemCtgGr());
+ itemRequest.setPartAdd("0");
+ itemRequest.setDelFlg("0");
+ break;
+ }
+ }
+
+ // 아이템 BOM Header인 경우 컴포넌트 등록 처리
+ if ("ERLA".equals(itemRequest.getItemCtgGr())) {
+ List itemBomList =
+ estimateMapper.selectItemMasterBomList(itemRequest.getItemId());
+
+ int k = 1;
+ for (ItemResponse itemResponse : itemBomList) {
+ ItemRequest bomItem = new ItemRequest();
+
+ bomItem.setPaDispOrder(String.valueOf(dispOrder));
+ bomItem.setDispOrder(String.valueOf(dispOrder + k++));
+ bomItem.setItemId(itemResponse.getItemId());
+ bomItem.setItemNo(itemResponse.getItemNo());
+ bomItem.setItemName(itemResponse.getItemName());
+ bomItem.setUnit(itemResponse.getUnit());
+ bomItem.setPnowW(itemResponse.getPnowW());
+ bomItem.setSpecification(itemResponse.getPnowW());
+ bomItem.setAmount(String.valueOf(Integer.parseInt(itemResponse.getBomAmount())
+ * Integer.parseInt(itemRequest.getAmount())));
+ bomItem.setBomAmount(itemResponse.getBomAmount());
+ bomItem.setUnitPrice(itemResponse.getSalePrice());
+ bomItem.setSalePrice(itemResponse.getSalePrice());
+ bomItem.setFileUploadFlg(itemResponse.getFileUploadFlg());
+ bomItem.setPkgMaterialFlg(itemResponse.getPkgMaterialFlg());
+ bomItem.setOpenFlg("0");
+ bomItem.setUnitOpenFlg("0");
+ bomItem.setDispCableFlg("0");
+ bomItem.setItemGroup(itemResponse.getItemGroup());
+ bomItem.setItemCtgGr(itemResponse.getItemCtgGr());
+ bomItem.setPartAdd("0");
+ bomItem.setDelFlg("0");
+
+ estimateBomList.add(bomItem);
+ }
+ }
+ }
+
+ // BOM 컴포넌트 추가
+ itemList.addAll(estimateBomList);
+
+ // [4]. 견적특이사항 관련 데이터 셋팅
+ NoteRequest noteRequest = new NoteRequest();
+ noteRequest.setArrItemId(arrItemId);
+ noteRequest.setSchSpnTypeCd("COMM");
+ List noteList = estimateMapper.selectEstimateNoteList(noteRequest);
+ noteRequest.setSchSpnTypeCd("PROD");
+ List noteItemList = estimateMapper.selectEstimateNoteItemList(noteRequest);
+
+ // 견적특이사항 코드
+ String estimateOptions = "";
+ for (NoteResponse noteResponse : noteList) {
+ if ("ATTR001".equals(noteResponse.getCode())) {
+ estimateOptions += !StringUtils.isEmpty(estimateOptions) ? splitStr : "";
+ estimateOptions += "ATTR001"; // 공통 필수체크
+ } else if ("ATTR002".equals(noteResponse.getCode())) {
+ estimateOptions += !StringUtils.isEmpty(estimateOptions) ? splitStr : "";
+ estimateOptions += "ATTR002"; // YJOD 필수체크
+ } else if ("ATTR003".equals(noteResponse.getCode())) {
+ estimateOptions += !StringUtils.isEmpty(estimateOptions) ? splitStr : "";
+ estimateOptions += "ATTR003"; // 출력제어시간 기본 체크
+ } else if ("ATTR004".equals(noteResponse.getCode())
+ && "1".equals(estimateRequest.getNorthArrangement())) {
+ estimateOptions += !StringUtils.isEmpty(estimateOptions) ? splitStr : "";
+ estimateOptions += "ATTR004"; // 북면설치 체크
+ } else if ("ATTR005".equals(noteResponse.getCode())
+ && "1".equals(objectResponse != null ? objectResponse.getSaltAreaFlg() : "")) {
+ estimateOptions += !StringUtils.isEmpty(estimateOptions) ? splitStr : "";
+ estimateOptions += "ATTR005"; // 염해지역 체크
+ } else if ("ATTR006".equals(objectResponse != null ? objectResponse.getColdRegionFlg() : "")
+ && "1".equals(estimateRequest.getNorthArrangement())) {
+ estimateOptions += !StringUtils.isEmpty(estimateOptions) ? splitStr : "";
+ estimateOptions += "ATTR006"; // 적설지역 체크
+ } else if ("ATTR007".equals(noteResponse.getCode())) {
+ estimateOptions += !StringUtils.isEmpty(estimateOptions) ? splitStr : "";
+ estimateOptions += "ATTR007"; // 지붕재치수, 레이아웃 기본 체크
+ } else if ("ATTR008".equals(noteResponse.getCode())) {
+ estimateOptions += !StringUtils.isEmpty(estimateOptions) ? splitStr : "";
+ estimateOptions += "ATTR008"; // 별도비용 기본 체크
+ } else if ("ATTR009".equals(noteResponse.getCode())) {
+ estimateOptions += !StringUtils.isEmpty(estimateOptions) ? splitStr : "";
+ estimateOptions += "ATTR009"; // 과적재 기본 체크
+ }
+ }
+ for (NoteResponse noteResponse : noteItemList) {
+ if (estimateOptions.indexOf(noteResponse.getCode()) < 0) {
+ estimateOptions += !StringUtils.isEmpty(estimateOptions) ? splitStr : "";
+ estimateOptions += noteResponse.getCode();
+ }
+ }
+ estimateRequest.setEstimateOption(estimateOptions);
+
+ // 아이템별 특이사항 코드 체크
+ for (ItemRequest itemRequest : itemList) {
+ String specialNoteCd = "";
+ for (NoteResponse noteResponse : noteItemList) {
+ if (itemRequest.getItemId().equals(noteResponse.getItemId())) {
+ specialNoteCd += !StringUtils.isEmpty(specialNoteCd) ? splitStr : "";
+ specialNoteCd += noteResponse.getCode();
+ }
+ }
+ itemRequest.setSpecialNoteCd(specialNoteCd);
+ }
+
+ // 첨부파일 초기화
+ FileRequest fileRequest = new FileRequest();
+ fileRequest.setObjectNo(estimateRequest.getObjectNo());
+ fileRequest.setPlanNo(estimateRequest.getPlanNo());
+ fileRequest.setUserId(estimateRequest.getUserId());
+ fileMapper.deleteFile(fileRequest);
+
+ // 아이템 목록 필수 값 체크
+ BigDecimal capacity = BigDecimal.ZERO;
+ String moduleModel = "";
+ String pcTypeNo = "";
+ for (ItemRequest itemRequest : itemList) {
+ if (StringUtils.isEmpty(itemRequest.getDispOrder())) {
+ throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
+ message.getMessage("common.message.required.data", "Display Order"));
+ }
if (StringUtils.isEmpty(itemRequest.getItemId())) {
- throw new QcastException(
- ErrorCode.INVALID_INPUT_VALUE,
- message.getMessage("common.message.required.data", "Item ID"));
+ throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
+ message.getMessage("common.message.required.data", "Item ID"));
}
if (StringUtils.isEmpty(itemRequest.getAmount())) {
- throw new QcastException(
- ErrorCode.INVALID_INPUT_VALUE,
- message.getMessage("common.message.required.data", "Item Amount"));
+ throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
+ message.getMessage("common.message.required.data", "Item Amount"));
}
// 수량
- BigDecimal amount =
- new BigDecimal(
+ BigDecimal amount = new BigDecimal(
StringUtils.isEmpty(itemRequest.getAmount()) ? "0" : itemRequest.getAmount());
// 아이템용량
- BigDecimal pnowW =
- new BigDecimal(
+ BigDecimal pnowW = new BigDecimal(
StringUtils.isEmpty(itemRequest.getPnowW()) ? "0" : itemRequest.getPnowW());
// 모듈/PC 체크
@@ -438,167 +1232,147 @@ public class EstimateService {
estimateRequest.setModuleModel(moduleModel);
estimateRequest.setPcTypeNo(pcTypeNo);
- // 물건정보 수정
- if (!StringUtils.isEmpty(estimateRequest.getObjectName())
- || !StringUtils.isEmpty(estimateRequest.getSurfaceType())
- || !StringUtils.isEmpty(estimateRequest.getSetupHeight())
- || !StringUtils.isEmpty(estimateRequest.getStandardWindSpeedId())
- || !StringUtils.isEmpty(estimateRequest.getSnowfall())) {
- estimateMapper.updateObject(estimateRequest);
- }
-
- // 견적서 정보 수정
- estimateRequest.setPriceCd("UNIT_PRICE");
- estimateMapper.updateEstimate(estimateRequest);
-
- // 도면 작성일 경우에만 지붕재 데이터 초기화 후 저장
- if ("1".equals(estimateRequest.getDrawingFlg())) {
- // 견적서 지붕면/아이템 제거
- estimateMapper.deleteEstimateRoofList(estimateRequest);
- estimateMapper.deleteEstimateRoofItemList(estimateRequest);
-
- // 견적서 지붕면/아이템 신규 추가
- for (RoofRequest roofRequest : roofList) {
- roofRequest.setObjectNo(estimateRequest.getObjectNo());
- roofRequest.setPlanNo(estimateRequest.getPlanNo());
- roofRequest.setUserId(estimateRequest.getUserId());
-
- estimateMapper.insertEstimateRoof(roofRequest);
-
- List roofItemList = roofRequest.getRoofItemList();
- for (ItemRequest itemRequest : roofItemList) {
- itemRequest.setObjectNo(estimateRequest.getObjectNo());
- itemRequest.setPlanNo(estimateRequest.getPlanNo());
- itemRequest.setRoofNo(roofRequest.getRoofNo());
-
- estimateMapper.insertEstimateRoofItem(itemRequest);
- }
- }
- }
-
+ // 견적서 정보 초기화
+ estimateMapper.updateEstimateReset(estimateRequest);
+ estimateMapper.updateEstimateInfoReset(estimateRequest);
// 견적서 모든 아이템 제거
estimateMapper.deleteEstimateItemList(estimateRequest);
+ estimateMapper.deleteEstimateInfoItemList(estimateRequest);
// 견적서 아이템 신규 추가
+ String hisNo = estimateMapper.selectEstimateItemHisNo(estimateRequest); // 아이템 히스토리 번호 조회
for (ItemRequest itemRequest : itemList) {
+ itemRequest.setHisNo(hisNo);
itemRequest.setObjectNo(estimateRequest.getObjectNo());
itemRequest.setPlanNo(estimateRequest.getPlanNo());
+ itemRequest.setAmount(
+ !StringUtils.isEmpty(itemRequest.getAmount()) ? itemRequest.getAmount() : "0");
+ itemRequest.setSalePrice(
+ !StringUtils.isEmpty(itemRequest.getSalePrice()) ? itemRequest.getSalePrice() : "0");
+ itemRequest.setBomAmount(
+ !StringUtils.isEmpty(itemRequest.getBomAmount()) ? itemRequest.getBomAmount() : "0");
itemRequest.setPartAdd(
- !StringUtils.isEmpty(itemRequest.getPartAdd()) ? itemRequest.getPartAdd() : "0");
+ !StringUtils.isEmpty(itemRequest.getPartAdd()) ? itemRequest.getPartAdd() : "0");
+ itemRequest.setOpenFlg(
+ !StringUtils.isEmpty(itemRequest.getOpenFlg()) ? itemRequest.getOpenFlg() : "0");
+ itemRequest.setUnitOpenFlg(
+ !StringUtils.isEmpty(itemRequest.getUnitOpenFlg()) ? itemRequest.getUnitOpenFlg() : "0");
itemRequest.setItemChangeFlg(
- !StringUtils.isEmpty(itemRequest.getItemChangeFlg())
- ? itemRequest.getItemChangeFlg()
- : "0");
+ !StringUtils.isEmpty(itemRequest.getItemChangeFlg()) ? itemRequest.getItemChangeFlg()
+ : "0");
itemRequest.setUserId(estimateRequest.getUserId());
- estimateMapper.insertEstimateItem(itemRequest);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
+ estimateMapper.insertEstimateItemHis(itemRequest);
- public EstimateResponse insertEstimateCopy(EstimateRequest estimateRequest) throws Exception {
- // Validation
- if (StringUtils.isEmpty(estimateRequest.getObjectNo())) {
- throw new QcastException(
- ErrorCode.INVALID_INPUT_VALUE,
- message.getMessage("common.message.required.data", "Object No"));
- }
- if (StringUtils.isEmpty(estimateRequest.getPlanNo())) {
- throw new QcastException(
- ErrorCode.INVALID_INPUT_VALUE,
- message.getMessage("common.message.required.data", "Plan No"));
- }
-
- EstimateResponse response = new EstimateResponse();
-
- // [1]. 총 플랜 목록 조회 및 제약조건 처리 (플랜 10개까지만 등록)
- PlanRequest planRequest = new PlanRequest();
- planRequest.setObjectNo(estimateRequest.getObjectNo());
- List