Compare commits

...

58 Commits

Author SHA1 Message Date
이주일
e34fa87214 fix: ACI 배송조회 주문번호에 주문배송번호 추가 2026-07-22 17:33:56 +09:00
이주일
796f20b604 fix: ACI POD 배송중 판단 기준 현지출발로 변경 2026-07-10 00:47:02 +09:00
이주일
10b826fb38 fix: ACI POD 미래 입고시간 배송중 전환 방지 2026-07-10 00:11:18 +09:00
이주일
c13bbd7e2d fix: ACI POD 배송중 추적 및 배송완료 전환 보정 2026-07-09 22:07:03 +09:00
이주일
a75cfef5ba fix: ACI 출고 배치 출고고유번호 채번 오류 수정 2026-07-09 16:31:26 +09:00
이대환
fee1ff461c fix : 출고 상태 변경 (배송처리)
(cherry picked from commit 303f28b7ed)
2026-07-06 14:12:29 +09:00
이대환
d8d0608606 fix : 출고 상태 변경 (배송처리)
(cherry picked from commit 8080bffb56)
2026-07-06 11:41:54 +09:00
이대환
0c7657ac9c fix : 출고 상태 변경
(cherry picked from commit 8c856a53e5)
2026-07-03 10:11:18 +09:00
이대환
40cf536861 fix : 출고 상태 변경
(cherry picked from commit 6df5f7d280)
2026-07-03 10:11:14 +09:00
이대환
e4ea1be68f fix : 출고 상태 변경
(cherry picked from commit 9f365228bd)
2026-07-02 17:46:58 +09:00
이대환
efa7954d7f fix : 출고 상태 변경
(cherry picked from commit 11aa9b0882)
2026-07-02 11:42:38 +09:00
이대환
e6f07e3663 fix : 출고 상태 변경
(cherry picked from commit 101d01ea2c)
2026-06-10 13:37:39 +09:00
이대환
2beda0b881 출고 상태 변경
(cherry picked from commit f2049bc49d)
2026-06-10 11:40:43 +09:00
이주일
dc77dfd5f0 Merge remote-tracking branch 'private/dev' into dev 2026-05-26 18:23:37 +09:00
이주일
88db1cc9f5 fix: insertOrderLog DLIVY_INNB 파라미터 수정 (dlivyCdNb → dlivyInnb) 2026-05-26 18:20:35 +09:00
이주일
1e8ea795d8 Merge remote-tracking branch 'private/dev' into dev 2026-05-26 16:18:47 +09:00
claudecode
9b4ba72134 refactor: AciDlivyMapper mapper.gcone 이동, XML gcone 이동, GconeSourceConfig 정리, AciDlivyVO 삭제 2026-05-26 16:12:00 +09:00
이주일
9b98beb95a Merge branch 'dev' of http://115.68.207.90:3690/jilee1202/sake_private into dev 2026-05-26 14:51:22 +09:00
claudecode
5b4771b0c7 fix: AciDlivyMapper.xml namespace 수정 (acidlivy → acidlivy.tasklet) 2026-05-26 14:43:36 +09:00
이주일
c6b6e614b0 Merge branch 'dev' of http://115.68.207.90:3690/jilee1202/sake_private into dev 2026-05-26 14:37:19 +09:00
claudecode
aeabfd4af1 fix: AciDlivyMapper.xml AciDlivyOrderVO 클래스 경로 수정 (acidlivy → acidlivy.vo) 2026-05-26 14:16:25 +09:00
이주일
5574c9cfcf Merge branch 'dev' of http://115.68.207.90:3690/jilee1202/sake_private 2026-05-26 13:40:39 +09:00
claudecode
75feae6505 fix: AciDlivyJobConfig JobBuilder/StepBuilder 이름 문자열 따옴표 누락 수정 2026-05-26 13:34:26 +09:00
claudecode
c9b69251bc refactor: acidlivy 패키지 구조 리팩토링 (jobs/tasklet/vo) 및 한글 주석 UTF-8 수정 2026-05-26 13:08:54 +09:00
claudecode
1d278bca48 fix: gc_goods_export DLIVY_INNB 오류 수정 - ORDR_DLVY_INNB 사용, dlivyCdNb INSERT 전 생성 2026-05-26 11:59:26 +09:00
이주일
2a3cf05c09 Merge branch 'dev' of http://115.68.207.90:3690/jilee1202/sake_private
 Conflicts:
	src/main/resources/application-dev.yaml
2026-05-26 11:31:16 +09:00
Claude
1322a2ffbb feat: AciDlivyTasklet processDelivery 수정 - 0건 스킵, dlivyCdNb Java 통일, orderLogMap 누락값 추가 2026-05-25 03:25:30 +09:00
이주일
4c500550d9 TASK_BATCH_ACIDLIVY: AciDlivyTasklet 토큰 발급 로직 추가
- 배치 시작 시 issueAciKeyToken() 호출하여 ACIKey 토큰 발급
- yaml 고정 apikey는 토큰 발급 시 APIKEY Header로만 사용
- 발급받은 토큰은 메모리에만 유지, ACIKey Header에 세팅하여 API 호출
- acikey.url 주입 추가
2026-05-25 02:29:50 +09:00
이주일
f7eaac1ca8 [TASK_BATCH_ACIDLIVY] application-dev.yaml BOM 및 ^M 제거 2026-05-25 02:13:04 +09:00
이주일
2ef2304947 [TASK_BATCH_ACIDLIVY] GconeSourceConfig MapperScan/setMapperLocations acidlivy 추가 2026-05-25 01:47:35 +09:00
이주일
4c6dd27e9f [TASK_BATCH_ACIDLIVY] BOM 제거 2026-05-25 01:19:19 +09:00
이주일
c9f2843a10 [TASK_BATCH_ACIDLIVY] ACI 배송자료 조회 및 출고처리 배치 신규 생성 - AciDlivyJobConfig/Tasklet/Mapper/VO 생성, application-dev.yaml aci.dlivy.api-url 추가 2026-05-24 21:22:50 +09:00
이주일
6cbdc36c71 fix: GconeSourceConfig 배치 스키마 초기화 추가 (BATCH_JOB_SEQ 시퀀스 테이블 자동 생성)
- batchSchemaInitializer 빈 추가: schema-drop-mysql.sql + schema-mysql.sql 실행
- jobRepository에 @DependsOn(batchSchemaInitializer) 추가하여 초기화 순서 보장
- spring.batch.jdbc.initialize-schema 미적용 우회 (커스텀 JobRepository 빈 충돌)
2026-05-23 11:24:12 +09:00
이주일
0bf03de005 fix: Spring Batch MySQL 시퀀스 채번 오류 수정 (BATCH_JOB_INSTANCE Duplicate entry 0)
- GconeSourceConfig.jobRepository: setDatabaseType(MYSQL) 추가
- application-dev.yaml: spring.batch.jdbc.initialize-schema=always 추가 (BATCH_JOB_SEQ 등 시퀀스 테이블 자동 생성)
2026-05-23 11:17:23 +09:00
f46f0ad597 Update src/main/java/com/wb/domainserver/acikey/AciKeyTokenTasklet.java 2026-05-23 11:08:00 +09:00
c4ac3c8cf1 Update src/main/java/com/wb/domainserver/acikey/AciKeyTokenTasklet.java 2026-05-23 11:01:35 +09:00
01471c650e Update src/main/java/com/wb/domainserver/acikey/AciKeyTokenTasklet.java 2026-05-23 10:37:29 +09:00
이주일
1d9c29f7bc fix: application-dev.yaml reviewbo URL 추가 (RESRC_URL 플레이스홀더 오류 해결) 2026-05-23 10:23:49 +09:00
이주일
14f90c8d4b fix: application-dev.yaml gconedatasource 설정 추가 (GconeSourceConfig JobRepository 초기화 오류 해결) 2026-05-23 10:21:12 +09:00
이주일
34acf68408 refactor: BatchServerConfig Keycloak/OAuth2 코드 제거, 단순 permitAll 설정으로 교체 2026-05-23 10:10:29 +09:00
이주일
d7cc157171 feat: ACIKey 토큰 발급 테스트 배치 추가 (TASK_BATCH_ACIKEY)
- acikey 패키지 신규 생성
- AciKeyTokenJobConfig.java: Job/Step 설정
- AciKeyTokenTasklet.java: API 호출 및 토큰 확인 로직
- application-dev.yaml: acikey 설정값 추가 (url/userid/apikey)
2026-05-23 03:33:28 +09:00
7a3484cdcf fix: 잘못 생성된 파일명 삭제 및 application-dev.yaml 재생성 2026-05-23 00:06:28 +09:00
933a7e65e4 Update src/main/resources/server: port: 18211 spring: main: allow-bean-definition-overriding: true datasource: defaultdatasource: jdbc-url: jdbc:mysql:/10.2.3.97:3306/sake?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul&autoReconnect=true&allowMultiQuer 2026-05-22 23:56:24 +09:00
58319f620f Update Jenkinsfile 2026-05-22 23:54:45 +09:00
6ae3b3cf6a Update src/main/resources/application-dev.yaml 2026-05-22 23:38:57 +09:00
Claude
8c655724c8 DEPLOY_FILE/BACKUP_FILE APP_NAME ?? ??? ?? 2026-05-22 19:50:00 +09:00
Claude
9bd064bea2 DEPLOY_FILE/BACKUP_FILE APP_NAME ?? ??? ?? 2026-05-22 19:50:00 +09:00
Claude
688e840fd0 Merge dev: APP_NAME gcone_batch? ?? ? ?? ?? 2026-05-22 19:41:57 +09:00
Claude
7e0a156ac4 APP_NAME gcone_batch? ?? ? ?? ?? 2026-05-22 19:41:32 +09:00
Claude Code
d8f88245e9 Merge dev into master: SSH_KEY_PATH ??? 2026-05-22 19:34:02 +09:00
Claude Code
73be94a965 SSH_KEY_PATH ??? 2026-05-22 19:34:02 +09:00
Claude Code
c45536969d Merge dev into master: SERVER_IP ????? ?? 2026-05-22 19:29:18 +09:00
Claude Code
16d2aebf88 SERVER_IP ????? ?? 2026-05-22 19:29:18 +09:00
Claude Code
d9b547644d Merge dev into master: sshCommand ???? Jenkinsfile ?? 2026-05-22 19:14:23 +09:00
Claude Code
4a5b425ee8 sshCommand ???? Jenkinsfile ?? 2026-05-22 19:14:23 +09:00
Claude Code
a9bbda712a Merge dev into master: gradlew ?? ?? ?? 2026-05-22 18:52:01 +09:00
Claude Code
a1742e26ae gradlew ?? ?? ?? 2026-05-22 18:52:01 +09:00
Claude Code
2fc205ad4b Merge dev into master: Jenkins ?? ????? Jenkinsfile ?? 2026-05-22 18:05:39 +09:00
14 changed files with 1946 additions and 238 deletions

109
Jenkinsfile vendored
View File

@@ -6,23 +6,25 @@ pipeline {
environment {
// 서버 정보
SERVER_IP = '115.68.232.36'
SERVER_IP = '10.2.1.76'
SERVER_USER = 'tomcat'
// 앱 정보
APP_NAME = 'sake_batch'
TOMCAT_HOME = '/usr/local/tomcat'
APP_NAME = 'gcone_batch'
TOMCAT_HOME = '/usr/local/tomcat_dev'
DEPLOY_PATH = "${TOMCAT_HOME}/webapps"
BACKUP_PATH = "${TOMCAT_HOME}/backup"
DEPLOY_FILE = "${TOMCAT_HOME}/webapps/${APP_NAME}.war"
BACKUP_FILE = "${TOMCAT_HOME}/backup/${APP_NAME}.war.bak"
// 자격증명
SSH_CREDENTIAL = 'op-batch-servers-ssh'
// SSH 키 (쇼핑몰과 동일하게 주석 처리 - jenkins 기본 키 사용)
SSH_KEY_PATH = "/var/lib/jenkins/.ssh/batch_server_key"
}
stages {
stage('Checkout') {
steps {
git branch: 'master',
git branch: 'dev',
credentialsId: 'itfirst',
url: 'http://115.68.207.90:3000/itfirst/sake_batch.git'
}
@@ -30,24 +32,28 @@ pipeline {
stage('Build') {
steps {
sh 'chmod +x gradlew'
sh './gradlew clean war'
}
}
stage('Backup') {
steps {
sshagent(credentials: ["${SSH_CREDENTIAL}"]) {
sh """
ssh ${SERVER_USER}@${SERVER_IP} '
mkdir -p ${BACKUP_PATH}
if [ -f ${DEPLOY_PATH}/${APP_NAME}.war ]; then
cp ${DEPLOY_PATH}/${APP_NAME}.war \
${BACKUP_PATH}/${APP_NAME}.war.bak
echo "백업 완료"
else
echo "백업 대상 없음 (최초 배포)"
fi
'
script {
sshCommand remote: [
name: 'Batch Server',
host: "${env.SERVER_IP}",
user: "${env.SERVER_USER}",
identityFile: "${env.SSH_KEY_PATH}",
allowAnyHosts: true
], command: """
if [ -f ${env.DEPLOY_FILE} ]; then
mkdir -p ${env.BACKUP_PATH}
cp ${env.DEPLOY_FILE} ${env.BACKUP_FILE}
echo '기존 WAR 파일 백업 완료'
else
echo '백업할 WAR 파일이 없습니다 (최초 배포)'
fi
"""
}
}
@@ -55,19 +61,27 @@ pipeline {
stage('Deploy') {
steps {
sshagent(credentials: ["${SSH_CREDENTIAL}"]) {
sh """
# WAR 전송
scp build/libs/${APP_NAME}.war \
${SERVER_USER}@${SERVER_IP}:${DEPLOY_PATH}/${APP_NAME}.war
script {
sshPut remote: [
name: 'Batch Server',
host: "${env.SERVER_IP}",
user: "${env.SERVER_USER}",
identityFile: "${env.SSH_KEY_PATH}",
allowAnyHosts: true
], from: "build/libs/${env.APP_NAME}.war",
into: "${env.DEPLOY_FILE}"
# Tomcat 재시작
ssh ${SERVER_USER}@${SERVER_IP} '
${TOMCAT_HOME}/bin/shutdown.sh || true
sleep 5
${TOMCAT_HOME}/bin/startup.sh
echo "Tomcat 재시작 완료"
'
sshCommand remote: [
name: 'Batch Server',
host: "${env.SERVER_IP}",
user: "${env.SERVER_USER}",
identityFile: "${env.SSH_KEY_PATH}",
allowAnyHosts: true
], command: """
${env.TOMCAT_HOME}/bin/shutdown.sh || true
sleep 5
${env.TOMCAT_HOME}/bin/startup.sh
echo 'Tomcat 재시작 완료'
"""
}
}
@@ -76,26 +90,29 @@ pipeline {
post {
success {
echo "✅ sake_batch 배포 완료"
echo '✅ sake_batch 개발 배포 완료'
}
failure {
sshagent(credentials: ["${SSH_CREDENTIAL}"]) {
sh """
ssh ${SERVER_USER}@${SERVER_IP} '
if [ -f ${BACKUP_PATH}/${APP_NAME}.war.bak ]; then
cp ${BACKUP_PATH}/${APP_NAME}.war.bak \
${DEPLOY_PATH}/${APP_NAME}.war
${TOMCAT_HOME}/bin/shutdown.sh || true
sleep 5
${TOMCAT_HOME}/bin/startup.sh
echo "롤백 완료"
else
echo "백업본 없음 - 롤백 불가"
fi
'
script {
sshCommand remote: [
name: 'Batch Server',
host: "${env.SERVER_IP}",
user: "${env.SERVER_USER}",
identityFile: "${env.SSH_KEY_PATH}",
allowAnyHosts: true
], command: """
if [ -f ${env.BACKUP_FILE} ]; then
cp ${env.BACKUP_FILE} ${env.DEPLOY_FILE}
${env.TOMCAT_HOME}/bin/shutdown.sh || true
sleep 5
${env.TOMCAT_HOME}/bin/startup.sh
echo '⚠️ 롤백 완료'
else
echo '⚠️ 백업본 없음 - 롤백 불가'
fi
"""
}
echo "❌ 배포 실패 - 롤백 처리됨"
echo '❌ 게빌 배포 실패 - 롤백 처리됨'
}
}
}

34
shell/aciDlivyJob_run.sh Executable file
View File

@@ -0,0 +1,34 @@
#!/bin/sh
JOB_NAME="aciDlivyJob"
HOST="10.2.1.76"
PORT="18211"
LOG_DIR="/home/batch_shell/logs"
LOG_FILE="${LOG_DIR}/${JOB_NAME}_$(date +%Y%m%d).log"
mkdir -p ${LOG_DIR}
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
echo "[${TIMESTAMP}] ===== ${JOB_NAME} 실행 시작 =====" >> ${LOG_FILE}
HTTP_CODE=$(curl -s -o /tmp/${JOB_NAME}_resp.tmp -w "%{http_code}" \
-X POST "http://${HOST}:${PORT}/gcone_batch/batch/run/${JOB_NAME}" \
-H "Content-Type: application/json")
BODY=$(cat /tmp/${JOB_NAME}_resp.tmp 2>/dev/null)
rm -f /tmp/${JOB_NAME}_resp.tmp
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
echo "[${TIMESTAMP}] HTTP Status: ${HTTP_CODE}" >> ${LOG_FILE}
echo "[${TIMESTAMP}] Response : ${BODY}" >> ${LOG_FILE}
if [ "${HTTP_CODE}" = "200" ]; then
echo "[${TIMESTAMP}] ${JOB_NAME} 실행 성공" >> ${LOG_FILE}
echo "Batch job ${JOB_NAME} executed successfully."
else
echo "[${TIMESTAMP}] ${JOB_NAME} 실행 실패 (HTTP ${HTTP_CODE})" >> ${LOG_FILE}
echo "Failed to execute batch job ${JOB_NAME}. HTTP Status: ${HTTP_CODE}"
fi
echo "[${TIMESTAMP}] ===== ${JOB_NAME} 실행 완료 =====" >> ${LOG_FILE}

View File

@@ -0,0 +1,52 @@
package com.wb.domainserver.acidlivy.jobs;
import com.wb.domainserver.acidlivy.tasklet.AciDlivyTasklet;
import com.wb.domainserver.acidlivy.tasklet.AciPodTasklet;
import lombok.RequiredArgsConstructor;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
// ACI 배송자료 조회 및 출고처리 배치 Job/Step 설정
@Configuration
@RequiredArgsConstructor
public class AciDlivyJobConfig {
private final JobRepository jobRepository;
private final PlatformTransactionManager transactionManager;
private final AciDlivyTasklet aciDlivyTasklet;
private final AciPodTasklet aciPodTasklet;
@Bean
public Job aciDlivyJob() {
return new JobBuilder("aciDlivyJob", jobRepository)
.start(aciDlivyStep())
.build();
}
@Bean
public Step aciDlivyStep() {
return new StepBuilder("aciDlivyStep", jobRepository)
.tasklet(aciDlivyTasklet, transactionManager)
.build();
}
@Bean
public Job aciPodJob() {
return new JobBuilder("aciPodJob", jobRepository)
.start(aciPodStep())
.build();
}
@Bean
public Step aciPodStep() {
return new StepBuilder("aciPodStep", jobRepository)
.tasklet(aciPodTasklet, transactionManager)
.build();
}
}

View File

@@ -0,0 +1,331 @@
package com.wb.domainserver.acidlivy.tasklet;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wb.domainserver.acidlivy.vo.AciDlivyOrderVO;
import com.wb.domainserver.mapper.gcone.AciDlivyMapper;
import com.wb.domainserver.util.dataaccess.DataMap;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.web.client.RestTemplate;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
// ACI 출고자료 조회 및 출고처리 배치 Tasklet
// 흐름: gc_order_shipping(ORDR_STTUS_CD=35) 조회 → 토큰 발급 → ACI API 호출 → BLNO 추출 → 출고처리
@Slf4j
@Component
@RequiredArgsConstructor
public class AciDlivyTasklet implements Tasklet {
private final AciDlivyMapper aciDlivyMapper;
private final PlatformTransactionManager transactionManager;
@Value("${acikey.url}")
private String aciKeyUrl;
@Value("${aci.dlivy.api-url}")
private String aciDlivyApiUrl;
@Value("${acikey.userid}")
private String userId;
@Value("${acikey.apikey}")
private String apiKey;
private static final String HDRYCPN_CD = "code22"; // ACI 택배사 코드
private static final String DLIVY_STTUS_CD = "55"; // 출고완료 코드
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {
log.info("[AciDlivyBatch] 배치 시작");
// 토큰 발급 (yaml 고정 apikey → ACIKey 토큰 발급, 메모리에만 유지)
String aciKeyToken = issueAciKeyToken();
// 대상 주문 목록 조회 (ORDR_STTUS_CD = '35')
List<AciDlivyOrderVO> targetList = aciDlivyMapper.selectAciDlivyTargetList();
log.info("[AciDlivyBatch] 대상 주문 건수: {}", targetList.size());
if (targetList.isEmpty()) {
log.info("[AciDlivyBatch] 처리할 대상 주문이 없습니다.");
return RepeatStatus.FINISHED;
}
int successCnt = 0;
int failCnt = 0;
int skipCnt = 0;
// 건별로 독립 트랜잭션 (REQUIRES_NEW) 으로 처리 - 한 건 실패가 다른 건에 영향 없도록
TransactionTemplate txTemplate = new TransactionTemplate(transactionManager);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
for (AciDlivyOrderVO orderVO : targetList) {
try {
// 건별 ACI 출고자료 API 호출 → BLNO 추출
String blno = callAciDlivyApi(orderVO, aciKeyToken);
if (blno == null) {
log.info("[AciDlivyBatch] 스킵 - ORDR_DLVY_INNB={}, ORDR_INNB={} (BLNO 없음 또는 조건 미충족)",
orderVO.getOrdrDlvyInnb(), orderVO.getOrdrInnb());
skipCnt++;
} else {
orderVO.setBlno(blno);
orderVO.setHdrycpnCd(HDRYCPN_CD);
orderVO.setDlivySttusCd(DLIVY_STTUS_CD);
// 건별 처리 (독립 트랜잭션)
txTemplate.execute(status -> {
processDelivery(orderVO);
return null;
});
successCnt++;
log.info("[AciDlivyBatch] 출고처리 완료 - ORDR_DLVY_INNB={}, BLNO={}",
orderVO.getOrdrDlvyInnb(), blno);
}
} catch (Exception e) {
log.error("[AciDlivyBatch] 출고처리 실패 - ORDR_DLVY_INNB={}, 오류={}",
orderVO.getOrdrDlvyInnb(), e.getMessage(), e);
failCnt++;
}
// 건별 API 호출 간격 유지 (과부하 방지 - 1초 대기)
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
break;
}
}
log.info("[AciDlivyBatch] 배치 완료 - 성공: {}, 실패: {}, 스킵: {}", successCnt, failCnt, skipCnt);
return RepeatStatus.FINISHED;
}
/**
* ACIKey 토큰 발급
* yaml 고정 apikey를 APIKEY Header로 사용하여 POST 호출 → 발급받은 토큰은 메모리에만 유지
*/
private String issueAciKeyToken() {
try {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("USERID", userId);
headers.set("APIKEY", apiKey);
HttpEntity<String> requestEntity = new HttpEntity<>("{}", headers);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.exchange(
aciKeyUrl,
HttpMethod.POST,
requestEntity,
String.class
);
ObjectMapper objectMapper = new ObjectMapper();
Map<String, Object> body = objectMapper.readValue(response.getBody(), Map.class);
String status = (String) body.get("Status");
String aciKey = (String) body.get("ACIKey");
String errMsg = (String) body.get("ErrMsg");
if ("success".equals(status) && aciKey != null) {
log.info("[AciDlivyBatch] ACIKey 토큰 발급 성공");
return aciKey;
} else {
throw new RuntimeException("ACIKey 토큰 발급 실패. Status: " + status + ", ErrMsg: " + errMsg);
}
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException("ACIKey 토큰 발급 오류", e);
}
}
/**
* ACI 출고자료 API 호출
* Content-Type이 text/html로 오는 경우를 대비해 JSON을 String으로 받아 ObjectMapper 파싱
* @param aciKeyToken 발급받은 ACIKey 토큰
* @return BLNO(운송장번호), 없으면 null
*/
private String callAciDlivyApi(AciDlivyOrderVO orderVO, String aciKeyToken) {
try {
HttpHeaders headers = new HttpHeaders();
headers.set("UserID", userId);
headers.set("ACIKey", aciKeyToken);
String aciOrderNumber = orderVO.getOrdrInnb() + "A" + orderVO.getOrdrDlvyInnb();
String url = aciDlivyApiUrl + "?searchType=1&orderNumber=" + aciOrderNumber;
HttpEntity<Void> entity = new HttpEntity<>(headers);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
log.debug("[AciDlivyBatch] API 응답 원문 (ORDR_INNB={}): {}", orderVO.getOrdrInnb(), response.getBody());
ObjectMapper objectMapper = new ObjectMapper();
Map<String, Object> body = objectMapper.readValue(response.getBody(), Map.class);
String status = (String) body.get("Status");
log.debug("[AciDlivyBatch] API Status={} (ORDR_INNB={})", status, orderVO.getOrdrInnb());
// 출고/발송완료 상태 검증 (정확한 Status값은 실제 응답 확인 후 조정 필요)
if (!"A10".equals(status)) {
log.info("[AciDlivyBatch] API 출고 미완료 - ORDR_INNB={}, Status={}", orderVO.getOrdrInnb(), status);
return null;
}
// Data 파싱 (단일 Map 또는 List<Map> 모두 대응)
Object dataObj = body.get("Data");
if (dataObj == null) {
return null;
}
List<Map<String, Object>> dataList;
if (dataObj instanceof List) {
dataList = (List<Map<String, Object>>) dataObj;
} else if (dataObj instanceof Map) {
dataList = List.of((Map<String, Object>) dataObj);
} else {
return null;
}
// OverseaSellerNo == ORDR_DLVY_INNB 매칭 → BLNO 추출
for (Map<String, Object> item : dataList) {
Object sellerNo = item.get("OverseaSellerNo");
if (sellerNo != null && orderVO.getOrdrDlvyInnb().equals(String.valueOf(sellerNo).trim())) {
Object blno = item.get("BLNO");
if (blno != null && !String.valueOf(blno).isBlank()) {
return String.valueOf(blno).trim();
}
}
}
log.info("[AciDlivyBatch] OverseaSellerNo 매칭 없음 - ORDR_DLVY_INNB={}", orderVO.getOrdrDlvyInnb());
return null;
} catch (Exception e) {
log.error("[AciDlivyBatch] API 호출 오류 - ORDR_INNB={}, 오류={}", orderVO.getOrdrInnb(), e.getMessage(), e);
return null;
}
}
/**
* 출고처리 (순서: gc_goods_export → gc_goods_export_item → gc_order_item_option
* → gc_order_shipping → gc_order → 출고로그 → 주문로그)
* gc_order_shipping/gc_order 상태변경은 모든 아이템 처리 완료 후 일괄 수행
*/
private void processDelivery(AciDlivyOrderVO orderVO) {
// ① 주문 상품 옵션 목록 먼저 조회 (0건이면 출고처리 전체 스킵)
List<DataMap> itemList = aciDlivyMapper.selectAciDlivyTargetItemList(orderVO);
if (itemList.isEmpty()) {
log.warn("[AciDlivyBatch] 주문 상품 옵션 없음 - 출고처리 스킵 (ORDR_DLVY_INNB={})", orderVO.getOrdrDlvyInnb());
return;
}
log.debug("[AciDlivyBatch] 주문 상품 옵션 수={} (ORDR_DLVY_INNB={})", itemList.size(), orderVO.getOrdrDlvyInnb());
// ② gc_goods_export INSERT/UPDATE
int exportCnt = aciDlivyMapper.selectGoodsExportCount(orderVO);
boolean newExport = exportCnt == 0;
if (exportCnt > 0) {
String dlivyInnb = aciDlivyMapper.selectGoodsExportDlivyInnb(orderVO);
if (dlivyInnb == null || dlivyInnb.isBlank()) {
throw new IllegalStateException("기존 출고번호 조회 실패 - ORDR_INNB=" + orderVO.getOrdrInnb()
+ ", ORDR_DLVY_INNB=" + orderVO.getOrdrDlvyInnb());
}
orderVO.setDlivyInnb(dlivyInnb);
aciDlivyMapper.updateGoodsExport(orderVO);
} else {
aciDlivyMapper.insertGoodsExport(orderVO);
}
String dlivyCdNb = "D" + new SimpleDateFormat("yyMMddHH").format(new Date()) + orderVO.getDlivyInnb();
orderVO.setDlivyCdNb(dlivyCdNb);
if (newExport) {
aciDlivyMapper.updateGoodsExportDlivyCdNb(orderVO);
}
// ③ 주문 상품 옵션별 처리 (gc_goods_export_item, gc_order_item_option)
for (DataMap itemMap : itemList) {
Object qyObj = itemMap.get("qy");
if (qyObj == null) continue;
int qy;
try {
qy = Integer.parseInt(String.valueOf(qyObj));
} catch (NumberFormatException e) {
continue;
}
if (qy <= 0) continue;
itemMap.put("sopmalCd", orderVO.getSopmalCd());
itemMap.put("ordrDlvyInnb", orderVO.getOrdrDlvyInnb());
itemMap.put("dlivyInnb", orderVO.getDlivyInnb());
itemMap.put("ordrInnb", orderVO.getOrdrInnb());
itemMap.put("hdrycpnCd", HDRYCPN_CD);
itemMap.put("invcnb", orderVO.getBlno());
itemMap.put("dlivySttusCd", DLIVY_STTUS_CD);
// gc_goods_export_item 처리
int exportItemCnt = aciDlivyMapper.selectGoodsExportItemCount(itemMap);
if (exportItemCnt > 0) {
aciDlivyMapper.updateGoodsExportItem(itemMap);
} else {
aciDlivyMapper.insertGoodsExportItem(itemMap);
}
// gc_order_item_option 상태 UPDATE
aciDlivyMapper.updateOrderItemOptionStatus(itemMap);
// 출고 로그 INSERT (건별)
DataMap exportLogMap = new DataMap();
exportLogMap.put("ordrInnb", orderVO.getOrdrInnb());
exportLogMap.put("ordrDlvyInnb", orderVO.getOrdrDlvyInnb());
exportLogMap.put("sopmalCd", orderVO.getSopmalCd());
exportLogMap.put("dlivyInnb", orderVO.getDlivyInnb());
exportLogMap.put("dlivyGoodsCdNb", itemMap.get("ordrGoodsOptnInnb"));
exportLogMap.put("reqstProcsSttusCd", DLIVY_STTUS_CD);
exportLogMap.put("mssageCn", "[ACI 배치] ORDR_INNB:" + orderVO.getOrdrInnb() + " 출고처리");
exportLogMap.put("regusr", 0);
aciDlivyMapper.insertGoodsExportLog(exportLogMap);
}
// ⑥ gc_order_shipping 상태 UPDATE (모든 item_option 처리 완료 후 일괄)
aciDlivyMapper.updateOrderShippingStatus(orderVO);
// ⑦ gc_order 상태 UPDATE (모든 shipping 처리 완료 후 일괄)
aciDlivyMapper.updateOrderStatus(orderVO);
// ⑧ 주문 로그 INSERT
DataMap orderLogMap = new DataMap();
orderLogMap.put("ordrInnb", orderVO.getOrdrInnb());
orderLogMap.put("sopmalCd", orderVO.getSopmalCd());
orderLogMap.put("logSj", "출고처리(" + dlivyCdNb + ")");
orderLogMap.put("logDtlCn", "[ACI 배치] 배치 출고처리 완료");
orderLogMap.put("aditInfoCn", null);
orderLogMap.put("dlivyInnb", orderVO.getDlivyInnb());
orderLogMap.put("refundCdNb", null);
orderLogMap.put("logDvCd", "01");
orderLogMap.put("dvCd", "BATCH");
orderLogMap.put("opetrNm", "ACI 배치");
orderLogMap.put("qy", 0);
orderLogMap.put("amt", 0);
aciDlivyMapper.insertOrderLog(orderLogMap);
}
}

View File

@@ -0,0 +1,528 @@
package com.wb.domainserver.acidlivy.tasklet;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wb.domainserver.acidlivy.vo.AciDlivyOrderVO;
import com.wb.domainserver.acidlivy.vo.BODlivyUpdtVO;
import com.wb.domainserver.exception.WnbtRestException;
import com.wb.domainserver.mapper.gcone.AciDlivyMapper;
import com.wb.domainserver.response.WnbtWebClientResponseHandler;
import com.wb.domainserver.util.communicate.SendToResrc;
import com.wb.domainserver.util.dataaccess.DataMap;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;
import java.math.BigInteger;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.*;
// ACI 배송자료 조회 및 배송처리 배치 Tasklet
// 흐름: gc_goods_export(DLIVY_STTUS_CD=55,65) 조회 -> 토큰 발급 -> ACI API 호출 -> BLNO 검증 -> 배송처리
@Slf4j
@Component
@RequiredArgsConstructor
public class AciPodTasklet implements Tasklet {
private final AciDlivyMapper AciDlivyMapper;
private final PlatformTransactionManager transactionManager;
@Autowired
WnbtWebClientResponseHandler responseHandler;
@Autowired
private SendToResrc sendToResrc;
@Value("${acikey.url}")
private String aciKeyUrl;
@Value("${aci.pod.api-url}")
private String aciPodApiUrl;
@Value("${acikey.userid}")
private String userId;
@Value("${acikey.apikey}")
private String apiKey;
private static final String DLIVY_STTUS_CD_EXPORT_COMPLETE = "55"; // 출고완료 코드
private static final String DLIVY_STTUS_CD_DELIVERY_IN_PROGRESS = "65"; // 배송중 코드
private static final String DLIVY_STTUS_CD_DELIVERY_COMPLETE = "75"; // 배송완료 코드
private static final DateTimeFormatter ACI_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {
log.info("[AciPodBatch] 배치 시작");
// 토큰 발급 (yaml 고정 apikey → ACIKey 토큰 발급, 메모리에만 유지)
String aciKeyToken = issueAciKeyToken();
// 대상 주문 목록 조회 (DLIVY_STTUS_CD IN ('55', '65'))
List<AciDlivyOrderVO> goodsExportList = AciDlivyMapper.selectGoodsExportList();
log.info("[AciPodBatch] 대상 출고 건수: {}", goodsExportList.size());
if (goodsExportList.isEmpty()) {
log.info("[AciPodBatch] 처리할 대상 출고건이 없습니다.");
return RepeatStatus.FINISHED;
}
int successCnt = 0;
int failCnt = 0;
int skipCnt = 0;
// 건별로 독립 트랜잭션 (REQUIRES_NEW) 으로 처리 - 한 건 실패가 다른 건에 영향 없도록
TransactionTemplate txTemplate = new TransactionTemplate(transactionManager);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
for (AciDlivyOrderVO dlivyOrderVO : goodsExportList) {
try {
// 건별 ACI 출고자료 API 호출
List<DataMap> podInfoList = callAciPodApi(dlivyOrderVO, aciKeyToken);
if (CollectionUtils.isEmpty(podInfoList)) {
skipCnt++;
} else {
boolean dlvyCompt = podInfoList.stream()
.anyMatch(podInfo -> !isBlank(podInfo, "dateDelivery"));
/*
* 배송중(65) 판단은 DATE_LOCALWHIN이 아니라 DATE_LOCALDEPARTURE 기준으로 처리한다.
*
* ACI POD API의 DATE_LOCALWHIN은 명세상 "현지 창고 입고 시간"이지만,
* 운영 확인 결과 실제 미입고/입고예정 건에도 배치 조회 시점과 거의 같은 시간이 채워질 수 있었다.
* 이 값만 보거나 현재시간과 비교하면 실제 현지 출발 전 주문이 배송중으로 전환되고,
* 주문/배송/옵션 상태와 주문 로그까지 잘못 생성된다.
*
* 따라서 DATE_LOCALWHIN은 상태 전환 트리거로 사용하지 않는다.
* 배송완료는 기존처럼 DATE_DELIVERY 값 존재 여부를 최우선으로 판단하고,
* 배송중은 현지 출발 시간인 DATE_LOCALDEPARTURE가 현재 시스템 시간보다 과거/현재인 경우에만 허용한다.
* DATE_LOCALDEPARTURE가 비어 있거나 미래 시간이면 아직 현지 출발 전으로 보고 상태 변경을 skip한다.
*/
boolean dlvyInPrg = podInfoList.stream()
.anyMatch(this::isLocalDeparture);
// 건별 배송처리
BODlivyUpdtVO boDlivyUpdtVO = new BODlivyUpdtVO();
boDlivyUpdtVO.setDlivyInnb(dlivyOrderVO.getDlivyInnb());
boDlivyUpdtVO.setDlivySttusCd(dlivyOrderVO.getDlivySttusCd());
boDlivyUpdtVO.setSopmalCd(dlivyOrderVO.getSopmalCd());
String currentDlivySttusCd = dlivyOrderVO.getDlivySttusCd();
if (dlvyCompt) {
boDlivyUpdtVO.setDlivySttusCd(DLIVY_STTUS_CD_DELIVERY_COMPLETE);
processDeliveryPod(boDlivyUpdtVO);
successCnt++;
} else if (dlvyInPrg && DLIVY_STTUS_CD_EXPORT_COMPLETE.equals(currentDlivySttusCd)) {
boDlivyUpdtVO.setDlivySttusCd(DLIVY_STTUS_CD_DELIVERY_IN_PROGRESS);
processDeliveryPod(boDlivyUpdtVO);
successCnt++;
} else {
if (dlvyInPrg && DLIVY_STTUS_CD_DELIVERY_IN_PROGRESS.equals(currentDlivySttusCd)) {
log.info("[AciPodBatch] 스킵 - 이미 배송중 상태 유지 - DLIVY_INNB={}, INVCNB={}",
dlivyOrderVO.getDlivyInnb(), dlivyOrderVO.getInvcnb());
} else {
log.info("[AciPodBatch] 스킵 - ACI 배송상태 판단 불가 - DLIVY_INNB={}, INVCNB={}",
dlivyOrderVO.getDlivyInnb(), dlivyOrderVO.getInvcnb());
}
skipCnt++;
}
}
} catch (Exception e) {
log.error("[AciPodBatch] 배송처리 실패 - DLIVY_INNB={}, 오류={}",
dlivyOrderVO.getDlivyInnb(), e.getMessage(), e);
failCnt++;
}
// 건별 API 호출 간격 유지 (과부하 방지 - 1초 대기)
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
break;
}
}
log.info("[AciPodBatch] 배치 완료 - 성공: {}, 실패: {}, 스킵: {}", successCnt, failCnt, skipCnt);
return RepeatStatus.FINISHED;
}
/**
* ACIKey 토큰 발급
* yaml 고정 apikey를 APIKEY Header로 사용하여 POST 호출 → 발급받은 토큰은 메모리에만 유지
*/
private String issueAciKeyToken() {
try {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("USERID", userId);
headers.set("APIKEY", apiKey);
HttpEntity<String> requestEntity = new HttpEntity<>("{}", headers);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.exchange(
aciKeyUrl,
HttpMethod.POST,
requestEntity,
String.class
);
ObjectMapper objectMapper = new ObjectMapper();
Map<String, Object> body = objectMapper.readValue(response.getBody(), Map.class);
String status = (String) body.get("Status");
String aciKey = (String) body.get("ACIKey");
String errMsg = (String) body.get("ErrMsg");
if ("success".equals(status) && aciKey != null) {
log.info("[AciPodBatch] ACIKey 토큰 발급 성공");
return aciKey;
} else {
throw new RuntimeException("ACIKey 토큰 발급 실패. Status: " + status + ", ErrMsg: " + errMsg);
}
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException("ACIKey 토큰 발급 오류", e);
}
}
/**
* ACI 배송자료 API 호출
* @param aciKeyToken 발급받은 ACIKey 토큰
* @return
*/
private List<DataMap> callAciPodApi(AciDlivyOrderVO orderVO, String aciKeyToken) {
try {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("UserID", userId);
headers.set("ACIKey", aciKeyToken);
Map<String, Object> body = new HashMap<>();
body.put("BLNO", List.of(orderVO.getInvcnb()));
HttpEntity<Map<String, Object>> apiRequest =
new HttpEntity<>(body, headers);
log.info("[AciPodBatch] API 요청: {}", apiRequest.toString());
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.exchange(
aciPodApiUrl,
HttpMethod.POST,
apiRequest,
String.class
);
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(response.getBody());
String status = getText(jsonNode, "Status");
log.info("[AciPodBatch] API 응답 Status: {}", status);
log.info("[AciPodBatch] API 응답 RstMsg: {}", getText(jsonNode, "RstMsg"));
if (!"success".equals(status)) {
log.info("[AciPodBatch] 스킵 - ACI 응답 Status 실패 - DLIVY_INNB={}, Status={}, ErrMsg={}",
orderVO.getDlivyInnb(), status, getText(jsonNode, "ErrMsg"));
return Collections.emptyList();
}
JsonNode podInfoNode = jsonNode.get("PODInfo");
if(podInfoNode != null && podInfoNode.isArray() && podInfoNode.size() > 0) {
List<DataMap> podInfoList = new ArrayList<>();
for(JsonNode podInfoItem : podInfoNode) {
String blno = getText(podInfoItem, "BLNO");
if (!Objects.equals(trimToEmpty(orderVO.getInvcnb()), trimToEmpty(blno))) {
log.info("[AciPodBatch] 스킵 - 요청 송장번호와 ACI 응답 BLNO 불일치 - DLIVY_INNB={}, INVCNB={}, BLNO={}",
orderVO.getDlivyInnb(), orderVO.getInvcnb(), blno);
continue;
}
DataMap PODInfo = new DataMap();
PODInfo.put("blno", blno);
PODInfo.put("dateLocalwhin", getText(podInfoItem, "DATE_LOCALWHIN"));
PODInfo.put("dateLocaldeparture", getText(podInfoItem, "DATE_LOCALDEPARTURE"));
PODInfo.put("dateIcnarrival", getText(podInfoItem, "DATE_ICNARRIVAL"));
PODInfo.put("dateCustomes", getText(podInfoItem, "DATE_CUSTOMES"));
PODInfo.put("dateDelivery", getText(podInfoItem, "DATE_DELIVERY"));
PODInfo.put("recipient", getText(podInfoItem, "RECIPIENT"));
PODInfo.put("wta", getText(podInfoItem, "WTA"));
PODInfo.put("wtdan", getText(podInfoItem, "WTDAN"));
List<DataMap> volumeList = new ArrayList<>();
JsonNode volumeNode = podInfoItem.get("VOLUME");
if (volumeNode != null && volumeNode.isArray()) {
for(JsonNode volumeItem : volumeNode) {
DataMap volume = new DataMap();
volume.put("width", getText(volumeItem, "WIDTH"));
volume.put("length", getText(volumeItem, "LENGTH"));
volume.put("height", getText(volumeItem, "HEIGHT"));
volumeList.add(volume);
}
}
PODInfo.put("volume", volumeList);
List<List> customsList = new ArrayList<>();
JsonNode customsNode = podInfoItem.get("CUSTOMS");
if (customsNode != null && customsNode.isArray()) {
for(JsonNode customsItem : customsNode) {
List<DataMap> customDetail = new ArrayList<>();
for(JsonNode customItem : customsItem) {
DataMap Custom = new DataMap();
Custom.put("dateEvent", getText(customItem, "DATE_EVENT"));
Custom.put("traceStatus", getText(customItem, "TRACE_STATUS"));
customDetail.add(Custom);
}
customsList.add(customDetail);
}
}
PODInfo.put("customs", customsList);
List<DataMap> localPodList = new ArrayList<>();
JsonNode localPodNode = podInfoItem.get("LOCAL_POD");
if (localPodNode != null && localPodNode.isArray()) {
for(JsonNode localPodItem : localPodNode) {
DataMap localPod = new DataMap();
localPod.put("traceStatus", getText(localPodItem, "TRACE_STATUS"));
localPod.put("dateEvent", getText(localPodItem, "DATE_EVENT"));
localPod.put("position", getText(localPodItem, "POSITION"));
localPodList.add(localPod);
}
}
PODInfo.put("localPod", localPodList);
podInfoList.add(PODInfo);
}
if (podInfoList.isEmpty()) {
log.info("[AciPodBatch] 스킵 - 요청 송장번호와 일치하는 ACI PODInfo 없음 - DLIVY_INNB={}, INVCNB={}",
orderVO.getDlivyInnb(), orderVO.getInvcnb());
} else {
log.info("[AciPodBatch] API 응답 PODInfo: {}", podInfoList);
}
return podInfoList;
} else {
log.info("[AciPodBatch] 스킵 - ACI PODInfo 없음 - DLIVY_INNB={}, ErrMsg={}",
orderVO.getDlivyInnb(), getText(jsonNode, "ErrMsg"));
return Collections.emptyList();
}
} catch (Exception e) {
log.error("[AciPodBatch] API 호출 오류 - DLIVY_INNB={}, 오류={}", orderVO.getDlivyInnb(), e.getMessage(), e);
return Collections.emptyList();
}
}
/**
* 배송처리
*/
private void processDeliveryPod(BODlivyUpdtVO boDlivyUpdtVO) {
boDlivyUpdtVO.setOpetrNm("시스템");
boDlivyUpdtVO.setMngrInnb("0");
boDlivyUpdtVO.setRegusr(0);
boDlivyUpdtVO.setUpdusr(0);
Map<String, Object> message = new HashMap<>();
try {
updateBODlvySttusUpdt(boDlivyUpdtVO);
// 메시지전송 ==================================================================
/*List<DataMap> smsList = mapper.selectDlivySmsListInqire(tempVO);
log.debug("메시지 발송 파라미터 확인 : {}", smsList);
if (!CollectionUtils.isEmpty(smsList)) {
System.out.println("smsList = " + smsList);
try {
DataMap result = mssageService.insertBOAtmcsndng(smsList);
} catch (Exception e) {
log.debug("메시지 발송 실패");
}
}*/
message.put("dlivyInnb", boDlivyUpdtVO.getDlivyInnb());
message.put("result", "success");
message.put("status", "0");
} catch (WnbtRestException wre){
message.put("status", "1");
log.error(wre.getMessage(), wre);
throw new WnbtRestException(wre.getMessage());
} catch (Exception e) {
message.put("status", "1");
message.put("errorMsg", e.getMessage());
log.error(e.getMessage(), e);
// throw new WnbtRestException(e.getMessage());
}
log.debug("result = {}", message);
}
/**
* 출고상태변경 팝업 > 출고 상태변경 ( '배송중', '배송완료' ) > 출고 및 주문 상태 업데이트
* @param tempVO
*/
@Transactional
public Map<String, Object> updateBODlvySttusUpdt(BODlivyUpdtVO tempVO) {
Map<String, Object> message = new HashMap<>();
List<DataMap> orderLogList = new ArrayList<>();
String logSj = "";
String logDtlCn = "";
String dlivyCdNb = "";
Integer ordrItemResult = 0;
String ordrInnb = "";
try {
log.debug("출고 상태 일괄 변경 tempVO : {}", tempVO);
ordrItemResult = AciDlivyMapper.updateBODlivySttusGetbackUpdtArr(tempVO);
List<DataMap> orderItemList = AciDlivyMapper.selectBOExportOrderItemInqire(tempVO);
Set<String> ordrDlvyInnbSet = new HashSet<>();
if (ordrItemResult > 0) {
if (orderItemList != null && orderItemList.size() > 0) {
ordrInnb = String.valueOf(orderItemList.get(0).get("ordrInnb"));
// 주문 상품 옵션 상태 부분배송중(60), 배송중(65) 처리 및 수량 수정
for (DataMap item : orderItemList) {
item.put("dlivySttusCd", tempVO.getDlivySttusCd());
item.put("updusr", tempVO.getUpdusr());
// 해당하는 주문상품옵션 배송중 수량 update 하는데 배송중 수량이 (판매가 - 결제취소 ) 수량보다 작으면 상태 부분배송중으로 업데이트
AciDlivyMapper.updateBOOrdrItemStatusUpdt6575(item); // TODO : 배송중 처리하면 개수안맞아도 걍 다 배송중 처리해야하는건지 확인 필요
dlivyCdNb = String.valueOf(item.get("dlivyCdNb"));
ordrDlvyInnbSet.add(item.get("ordrDlvyInnb").toString());
}
String[] ordrDlvyInnbArr = ordrDlvyInnbSet.toArray(new String[0]);
log.debug("주문 배송 set : {}", ordrDlvyInnbSet);
log.debug("주문 set : {}", ordrInnb);
// 주문 배송 상태 부분배송중(60), 배송중(65) 처리
DataMap dlvyVO = new DataMap();
dlvyVO.put("ordrDlvyInnbArr", ordrDlvyInnbArr);
dlvyVO.put("sopmalCd", tempVO.getSopmalCd());
dlvyVO.put("ordrInnb", ordrInnb);
dlvyVO.put("updusr", tempVO.getUpdusr());
Integer dlvyResult = 0;
dlvyResult = AciDlivyMapper.updateBOOrdrDlvyStatusUpdt6575(dlvyVO);
// 주문 상태 부분배송중(60), 배송중(65) 처리
if (dlvyResult > 0) {
DataMap ordrVO = new DataMap();
ordrVO.put("ordrInnb", ordrInnb);
ordrVO.put("sopmalCd", tempVO.getSopmalCd());
ordrVO.put("updusr", tempVO.getUpdusr());
AciDlivyMapper.updateBOOrdrStatusUpdt6575(ordrVO);
}
if ("65".equals(tempVO.getDlivySttusCd())) {
logSj = "배송중(" + dlivyCdNb + ")";
} else if ("75".equals(tempVO.getDlivySttusCd())) {
logSj = "배송완료(" + dlivyCdNb + ")";
}
logDtlCn = "[관리자] " + tempVO.getOpetrNm() + "이(가)" + logSj + " 처리를 하였습니다.";
} else {
if ("65".equals(tempVO.getDlivySttusCd())) {
logSj = "배송중(ERROR)";
} else if ("75".equals(tempVO.getDlivySttusCd())) {
logSj = "배송완료(ERROR)";
}
logDtlCn = "[관리자] " + tempVO.getOpetrNm() + "이(가)" + logSj + " 처리를 실패하였습니다.";
}
}
} catch (WnbtRestException wre){
throw new WnbtRestException(wre.getMessage());
} catch (Exception e ){
log.error(e.getMessage(), e);
} finally {
DataMap orderLogVO = new DataMap();
if (ordrInnb != null && !ordrInnb.equals("")) {
orderLogVO.put("ordrInnb", BigInteger.valueOf(Long.valueOf(ordrInnb)));
}
orderLogVO.put("sopmalCd", tempVO.getSopmalCd());
orderLogVO.put("logSj", logSj);
orderLogVO.put("logDtlCn", logDtlCn);
orderLogVO.put("logDvCd", "05"); // 출고
orderLogVO.put("dvCd", "02"); // 관리자
orderLogVO.put("opetrNm", tempVO.getOpetrNm());
orderLogVO.put("dlivyCdNb", tempVO.getDlivyInnb());
orderLogVO.put("qy", 0);
orderLogList.add(orderLogVO);
insertBODlivyOrdrLogRegist(orderLogList);
}
return message;
}
/**
* 출고 관련 주문 로그 등록
*/
public void insertBODlivyOrdrLogRegist(List<DataMap> logList) {
if (!CollectionUtils.isEmpty(logList)) {
for (DataMap logVO : logList) {
AciDlivyMapper.insertOrderLog(logVO);
}
}
}
/**
* 오브젝트 값 공백 확인
*/
private Boolean isBlank(DataMap podInfo, String key) {
return podInfo.get(key) == null
|| podInfo.get(key).toString().trim().isEmpty();
}
/**
* DATE_LOCALDEPARTURE 값이 현재 시스템 시간 기준으로 배송중 처리 가능한 현지 출발 시간인지 확인한다.
*/
private boolean isLocalDeparture(DataMap podInfo) {
if (isBlank(podInfo, "dateLocaldeparture")) {
return false;
}
String dateLocaldeparture = podInfo.get("dateLocaldeparture").toString().trim();
try {
LocalDateTime localDepartureDateTime = LocalDateTime.parse(dateLocaldeparture, ACI_DATE_TIME_FORMATTER);
return !localDepartureDateTime.isAfter(LocalDateTime.now());
} catch (DateTimeParseException e) {
log.info("[AciPodBatch] 스킵 - DATE_LOCALDEPARTURE 파싱 실패 - DATE_LOCALDEPARTURE={}", dateLocaldeparture);
return false;
}
}
private String getText(JsonNode jsonNode, String key) {
JsonNode valueNode = jsonNode.get(key);
if (valueNode == null || valueNode.isNull()) {
return "";
}
return valueNode.asText();
}
private String trimToEmpty(String value) {
return value == null ? "" : value.trim();
}
}

View File

@@ -0,0 +1,25 @@
package com.wb.domainserver.acidlivy.vo;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
// gc_order_shipping 조회 결과 및 ACI 출고처리 배치 복합 VO
@Getter
@Setter
@ToString
public class AciDlivyOrderVO {
// gc_order_shipping 조회 결과
private String ordrDlvyInnb; // 주문배송고유번호 (ACI OverseaSellerNo 매칭기준)
private String ordrInnb; // 주문번호 (API orderNumber 파라미터)
private String sopmalCd; // 쇼핑몰코드
private String invcnb; // 운송장번호
// ACI API 조회 및 처리
private String blno; // ACI 운송장번호 (BLNO)
private String hdrycpnCd; // 택배사코드 (고정값: code22)
private String dlivySttusCd; // 출고상태코드 (고정값: 55)
private String dlivyInnb; // 출고고유번호 (gc_goods_export.DLIVY_INNB)
private String dlivyCdNb; // 출고코드번호 ('D'+yyMMddHH+dlivyInnb 형식)
}

View File

@@ -0,0 +1,23 @@
package com.wb.domainserver.acidlivy.vo;
import lombok.Data;
@Data
public class BODlivyUpdtVO {
/**출고고유번호*/
private String dlivyInnb;
/**출고준비(45), 출고완료(55), 배송중(65), 배송완료(75)*/
private String dlivySttusCd;
/**쇼핑몰 코드*/
private String sopmalCd;
/** 처리자명 */
private String opetrNm;
/** 관리자 고유번호*/
private String mngrInnb;
/** 등록자*/
private int regusr;
/** 수정자*/
private int updusr;
}

View File

@@ -0,0 +1,35 @@
package com.wb.domainserver.acikey;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import lombok.RequiredArgsConstructor;
// ACIKey 토큰 발급 테스트 배치 - Job/Step 설정
@Configuration
@RequiredArgsConstructor
public class AciKeyTokenJobConfig {
private final JobRepository jobRepository;
private final PlatformTransactionManager transactionManager;
private final AciKeyTokenTasklet aciKeyTokenTasklet;
@Bean
public Job aciKeyTokenJob() {
return new JobBuilder("aciKeyTokenJob", jobRepository)
.start(aciKeyTokenStep())
.build();
}
@Bean
public Step aciKeyTokenStep() {
return new StepBuilder("aciKeyTokenStep", jobRepository)
.tasklet(aciKeyTokenTasklet, transactionManager)
.build();
}
}

View File

@@ -0,0 +1,82 @@
package com.wb.domainserver.acikey;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Map;
// ACIKey 토큰 발급 테스트 배치 - API 호출 및 토큰 확인 로직
@Slf4j
@Component
public class AciKeyTokenTasklet implements Tasklet {
@Value("${acikey.url}")
private String aciKeyUrl;
@Value("${acikey.userid}")
private String userId;
@Value("${acikey.apikey}")
private String apiKey;
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {
log.info("[AciKeyBatch] 배치 시작");
log.info("[AciKeyBatch] API URL: {}", aciKeyUrl);
try {
// Header 설정
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("USERID", userId);
headers.set("APIKEY", apiKey);
HttpEntity<String> requestEntity = new HttpEntity<>("{}", headers);
// API 호출
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.exchange(
aciKeyUrl,
HttpMethod.POST,
requestEntity,
String.class
);
ObjectMapper objectMapper = new ObjectMapper();
Map<String, Object> body = objectMapper.readValue(response.getBody(), Map.class);
log.info("[AciKeyBatch] 응답 전체: {}", body);
String status = (String) body.get("Status");
String aciKey = (String) body.get("ACIKey");
String errMsg = (String) body.get("ErrMsg");
if ("success".equals(status)) {
log.info("[AciKeyBatch] ✅ 토큰 발급 성공!");
log.info("[AciKeyBatch] ACIKey: {}", aciKey);
contribution.incrementWriteCount(1);
} else {
log.error("[AciKeyBatch] ❌ 토큰 발급 실패. Status: {}, ErrMsg: {}", status, errMsg);
throw new RuntimeException("ACIKey 토큰 발급 실패. Status: " + status + ", ErrMsg: " + errMsg);
}
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
log.error("[AciKeyBatch] ❌ API 호출 중 오류 발생: {}", e.getMessage(), e);
throw new RuntimeException("AciKey API 호출 오류", e);
}
log.info("[AciKeyBatch] 배치 종료");
return RepeatStatus.FINISHED;
}
}

View File

@@ -10,18 +10,28 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.init.DataSourceInitializer;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@Configuration
@EnableTransactionManagement
@MapperScan(
basePackages = "com.wb.domainserver.mapper.gcone",
basePackages = {
"com.wb.domainserver.mapper.gcone"
},
sqlSessionFactoryRef = "gconeSqlSessionFactory"
)
public class GconeSourceConfig {
@@ -45,7 +55,12 @@ public class GconeSourceConfig {
// SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
SqlSessionFactoryBean sessionFactory = new RefreshableSqlSessionFactoryBean();
sessionFactory.setDataSource(gconeDataSource);
sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mybatis/xml/mapper/gcone/**/*.xml"));
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
List<Resource> mapperLocations = new ArrayList<>();
mapperLocations.addAll(Arrays.asList(resolver.getResources("classpath*:mybatis/xml/mapper/gcone/**/*.xml")));
sessionFactory.setMapperLocations(mapperLocations.toArray(new Resource[0]));
sessionFactory.setConfigLocation(new PathMatchingResourcePatternResolver().getResource("classpath:mybatis/xml/config/mapper-config.xml"));
return sessionFactory.getObject();
}
@@ -61,14 +76,27 @@ public class GconeSourceConfig {
return new DataSourceTransactionManager(gconeDataSource);
}
// Spring Batch schema init (BATCH_JOB_SEQ etc)
@Bean(name = "batchSchemaInitializer")
public DataSourceInitializer batchSchemaInitializer(@Qualifier("gconeDataSource") DataSource gconeDataSource) {
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
populator.addScript(new ClassPathResource("org/springframework/batch/core/schema-drop-mysql.sql"));
populator.addScript(new ClassPathResource("org/springframework/batch/core/schema-mysql.sql"));
populator.setContinueOnError(true);
DataSourceInitializer initializer = new DataSourceInitializer();
initializer.setDataSource(gconeDataSource);
initializer.setDatabasePopulator(populator);
return initializer;
}
@Bean
public JobRepository jobRepository( @Qualifier("gconeTransactionManager") PlatformTransactionManager transactionManager, @Qualifier("gconeDataSource") DataSource gconeDataSource) throws Exception {
@DependsOn("batchSchemaInitializer")
public JobRepository jobRepository(@Qualifier("gconeTransactionManager") PlatformTransactionManager transactionManager, @Qualifier("gconeDataSource") DataSource gconeDataSource) throws Exception {
JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
factory.setDataSource(gconeDataSource);
factory.setTransactionManager(transactionManager);
factory.setDatabaseType("MYSQL");
factory.afterPropertiesSet();
return factory.getObject();
}
}
}

View File

@@ -0,0 +1,77 @@
package com.wb.domainserver.mapper.gcone;
import com.wb.domainserver.acidlivy.vo.AciDlivyOrderVO;
import com.wb.domainserver.acidlivy.vo.BODlivyUpdtVO;
import com.wb.domainserver.util.dataaccess.DataMap;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.List;
@Mapper
@Repository
public interface AciDlivyMapper {
/** 출고 대상 주문 목록 조회 (gc_order_shipping.ORDR_STTUS_CD = '35') */
List<AciDlivyOrderVO> selectAciDlivyTargetList();
/** 출고 대상 주문상품 옵션 조회 (gc_order_item_option) */
List<DataMap> selectAciDlivyTargetItemList(AciDlivyOrderVO vo);
/** gc_goods_export 존재 여부 */
int selectGoodsExportCount(AciDlivyOrderVO vo);
/** 기존 gc_goods_export 출고고유번호 조회 */
String selectGoodsExportDlivyInnb(AciDlivyOrderVO vo);
/** gc_goods_export INSERT */
int insertGoodsExport(AciDlivyOrderVO vo);
/** gc_goods_export UPDATE (기존 레코드 있음) */
int updateGoodsExport(AciDlivyOrderVO vo);
/** gc_goods_export DLIVY_CD_NB UPDATE (신규 출고 생성키 수신 후 호출) */
int updateGoodsExportDlivyCdNb(AciDlivyOrderVO vo);
/** gc_goods_export_item 존재 여부 */
int selectGoodsExportItemCount(DataMap itemMap);
/** gc_goods_export_item INSERT */
int insertGoodsExportItem(DataMap itemMap);
/** gc_goods_export_item UPDATE (기존 레코드 있음) */
int updateGoodsExportItem(DataMap itemMap);
/** gc_order_item_option 상태 UPDATE */
int updateOrderItemOptionStatus(DataMap itemMap);
/** gc_order_shipping 상태 UPDATE (하위 item_option 상태 기반으로 동적 결정) */
int updateOrderShippingStatus(AciDlivyOrderVO vo);
/** gc_order 상태 UPDATE (하위 shipping 상태 기반으로 동적 결정) */
int updateOrderStatus(AciDlivyOrderVO vo);
/** GC_GOODS_EXPORT_LOG INSERT */
int insertGoodsExportLog(DataMap logMap);
/** GC_ORDER_LOG INSERT */
int insertOrderLog(DataMap logMap);
/** 배송처리 대상 주문 목록 조회 (gc_goods_export.DLIVY_STTUS_CD IN ('55', '65')) */
List<AciDlivyOrderVO> selectGoodsExportList();
/** 출고 상태 일괄 변경 다건 */
public Integer updateBODlivySttusGetbackUpdtArr(BODlivyUpdtVO dlivyVO);
/** 출고에 해당하는 주문 상품 옵션 조회 */
public List<DataMap> selectBOExportOrderItemInqire(BODlivyUpdtVO dlivyVO);
/** 출고처리 후 주문 상품 옵션 상태 및 수량 수정 */
public void updateBOOrdrItemStatusUpdt6575(DataMap ordrItemVO);
/** 출고처리 후 주문 배송 상태 수정 */
public Integer updateBOOrdrDlvyStatusUpdt6575(DataMap ordrItemVO);
/** 출고처리 후 주문 상태 수정 */
public void updateBOOrdrStatusUpdt6575(DataMap ordrItemVO);
}

View File

@@ -1,56 +1,15 @@
package com.wb.domainserver.security.config;
import com.nimbusds.jwt.JWT;
import com.nimbusds.jwt.JWTParser;
import com.wb.domainserver.security.authentication.CustomAuthenticationEntryPoint;
import com.wb.domainserver.security.authentication.MemberJwtAuthenticationConverter;
import com.wb.domainserver.security.authorization.WnbtAuthorizationManager;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.Advisor;
import org.springframework.aop.support.JdkRegexpMethodPointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtDecoders;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.StringUtils;
import java.text.ParseException;
import static org.springframework.beans.factory.config.BeanDefinition.ROLE_INFRASTRUCTURE;
@EnableWebSecurity
@Configuration
@EnableMethodSecurity
public class BatchServerConfig {
private static final Logger logger = LoggerFactory.getLogger(BatchServerConfig.class);
@Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
String issuerUri;
@Autowired
private CustomAuthenticationEntryPoint authenticationEntryPoint;
@Bean
@Role(ROLE_INFRASTRUCTURE)
public Advisor authorizationManagerBeforeMethodInterception(WnbtAuthorizationManager<?> authorizationManager) {
JdkRegexpMethodPointcut pattern = new JdkRegexpMethodPointcut();
pattern.setPattern("com.wb.domainserver.*.controller.*");
return new AuthorizationManagerBeforeMethodInterceptor(pattern, authorizationManager);
}
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
@@ -58,53 +17,7 @@ public class BatchServerConfig {
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(authorize -> authorize
.anyRequest().permitAll()
)
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt
.decoder(jwtDecoder())
.jwtAuthenticationConverter(new MemberJwtAuthenticationConverter())
)
.bearerTokenResolver(this::customBearerTokenResolver)
)
.exceptionHandling(exceptions -> exceptions
.authenticationEntryPoint(authenticationEntryPoint)
);
return http.build();
}
private String customBearerTokenResolver(HttpServletRequest request) {
String authorization = request.getHeader("Authorization");
if (StringUtils.hasText(authorization) && authorization.startsWith("Bearer ")) {
return authorization.substring(7);
}
return null;
}
@Bean
public JwtDecoder jwtDecoder() {
return token -> {
String issuerUri = determineIssuerUri();
logger.debug("Determined issuerUri: {}", issuerUri);
JWT jwt = null;
try {
jwt = JWTParser.parse(token);
logger.debug("Token issuer: {}", jwt.getJWTClaimsSet().getIssuer());
} catch (ParseException e) {
throw new RuntimeException(e);
}
NimbusJwtDecoder jwtDecoder = JwtDecoders.fromOidcIssuerLocation(issuerUri);
return jwtDecoder.decode(token);
};
}
private String determineIssuerUri() {
return String.format(issuerUri);
}
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}

View File

@@ -1,133 +1,50 @@
server:
port: 18210
port: 18211
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: http://172.31.21.12:18320/realms/ADMIN
main:
allow-bean-definition-overriding: true
datasource:
defaultdatasource:
jdbc-url: jdbc:mysql://10.2.1.101:3306/sake?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul&autoReconnect=true&allowMultiQueries=true&characterEncoding=UTF-8&allowPublicKeyRetrieval=false&verifyServerCertificate=false
jdbc-url: jdbc:mysql://10.2.3.97:3306/sake?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul&autoReconnect=true&allowMultiQueries=true&characterEncoding=UTF-8&allowPublicKeyRetrieval=false&verifyServerCertificate=false
driver-class-name: com.mysql.cj.jdbc.Driver
username: sakeone
password: dnlfxjtm13@$
gconedatasource:
jdbc-url: jdbc:mysql://10.2.1.101:3306/sake?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul&autoReconnect=true&allowMultiQueries=true&characterEncoding=UTF-8&allowPublicKeyRetrieval=false&verifyServerCertificate=false
driver-class-name: com.mysql.cj.jdbc.Driver
username: sakeone
password: dnlfxjtm13@$
crmdatasource:
jdbc-url: jdbc:mysql://10.2.1.101:3306/sake?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul&autoReconnect=true&allowMultiQueries=true&characterEncoding=UTF-8&allowPublicKeyRetrieval=false&verifyServerCertificate=false
driver-class-name: com.mysql.cj.jdbc.Driver
username: sakeone
password: dnlfxjtm13@$
mqdatasource:
jdbc-url: jdbc:mysql://10.2.1.101:3306/sake?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul&autoReconnect=true&allowMultiQueries=true&characterEncoding=UTF-8&allowPublicKeyRetrieval=false&verifyServerCertificate=false
driver-class-name: com.mysql.cj.jdbc.Driver
username: sakeone
password: dnlfxjtm13@$
suredatasource:
jdbc-url: jdbc:mysql://10.2.1.101:3306/sake?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul&autoReconnect=true&allowMultiQueries=true&characterEncoding=UTF-8&allowPublicKeyRetrieval=false&verifyServerCertificate=false
jdbc-url: jdbc:mysql://10.2.3.97:3306/sake?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul&autoReconnect=true&allowMultiQueries=true&characterEncoding=UTF-8&allowPublicKeyRetrieval=false&verifyServerCertificate=false
driver-class-name: com.mysql.cj.jdbc.Driver
username: sakeone
password: dnlfxjtm13@$
batch:
jdbc:
initialize-schema: always
job:
enabled: true
enabled: false
mybatis:
mapper-locations: classpath*:mybatis/xml/mapper/**/*.xml
config-location: classpath:/mybatis/xml/config/mapper-config.xml
logging:
level:
org.springframework.web: debug
org.springframework.security: debug
org.springframework.security.oauth2: debug
com.wb.domainserver: debug
root: info
#Resource-server-url
url:
resource:
server:
gconebo: http://127.0.0.1:18500 #로컬로 돌릴때
# gconebo: http://127.0.0.1:18500 #개발서버 ip
gconebo: http://10.2.0.129:18500
reviewbo: http://127.0.0.1:18220
pagination:
page-unit: 10
page-size: 10
# 스윗트래커 url, api key
parcel:
trackApiUrl: https://info.sweettracker.co.kr
trackApiKey: R0sYxkD9vCxSXPKpStOY2w
# 네이버 스마트스토어 api key
smartstore:
sopmal:
stores:
#참다한홍삼
CH:
clientId: 48f8YgF04wTnge6taw9bsM
clientSecret: $2a$04$9Lfn5as5Vv/ZW5PAoKBBr.
#스마트 스토어 api url
smartstoreurl:
tokenurl: https://api.commerce.naver.com/external/v1/oauth2/token
orderlisturl: https://api.commerce.naver.com/external/v1/pay-order/seller/product-orders/last-changed-statuses
orderdetailurl : https://api.commerce.naver.com/external/v1/pay-order/seller/product-orders/query
#슈어엠 법인별 정보
surem:
sopmal:
#참다한홍삼
CH:
code: GC
data: (주)지씨바이오
erpcode: 01
userid: gcbio02
deptcode: KT-26E-ZS
yellowkey: cd51453ccf40ef714cadd5a7dd0506cd7340a986
#GC EP 파일 저장 경로
ep:
common:
fileupload:
uploaddirs:
path: /data/gcone/otrFile
naver: naver_third
google: googleEpXmlData
meta: metaEpXmlData
mobion: mobionEpXmlData
extensions:
xml: .xml
txt: .txt
cdnurl: http://img.groupit.co.kr
secret:
key: 1df5be2f7e90ae36
mail:
host: 10.2.1.76
port: 25
# SyncPos 요청경로
pos:
syncpos:
url: https://smart.easypos.net
uri: /servlet/EasyPosChannelSVL?cmd=TlxSyncHanamCMD
#kakao REST-API test key(AF-T 키임)
kakao:
restApi-key: "5376f8866ab47cc78095e00c00d00c3f"
#선물하기 URL
presenturl : dev.svc.gcone.co.kr
acikey:
url: https://acieshop.com/api/kr/token/
userid: UNIHAN
apikey: DC94F29AFD5399F54AAE7FAA46045B65
aci:
dlivy:
api-url: https://acieshop.com/api/kr/delivery/v1.5/detail/index.php
pod:
api-url: https://acieshop.com/api/kr/pod/v1.5/

View File

@@ -0,0 +1,646 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wb.domainserver.mapper.gcone.AciDlivyMapper">
<!-- 출고 대상 주문 목록 조회 (ORDR_STTUS_CD = '35' 상품준비중) -->
<select id="selectAciDlivyTargetList" resultType="com.wb.domainserver.acidlivy.vo.AciDlivyOrderVO">
<![CDATA[
SELECT
ORDR_DLVY_INNB,
ORDR_INNB,
SOPMAL_CD
FROM GC_ORDER_SHIPPING
WHERE ORDR_STTUS_CD = '35'
AND USE_AT = 'Y'
ORDER BY ORDR_DLVY_INNB
]]>
</select>
<!-- 출고 대상 주문상품 옵션 조회 -->
<!-- QY: 반품(85)을 제외한 미출고 수량, 출고 아이템/로그/상태 처리 기준 -->
<select id="selectAciDlivyTargetItemList"
parameterType="com.wb.domainserver.acidlivy.vo.AciDlivyOrderVO"
resultType="dataMap">
<![CDATA[
SELECT
OIOP.ORDR_GOODS_OPTN_INNB,
OIOP.SOPMAL_CD,
OIOP.OPTN_TY,
((OIOP.ORDR_QY - OIOP.STEP85) - (OIOP.STEP45 + OIOP.STEP55 + OIOP.STEP65 + OIOP.STEP75)) AS QY
FROM GC_ORDER_ITEM_OPTION OIOP
INNER JOIN GC_ORDER_SHIPPING SH
ON OIOP.SOPMAL_CD = SH.SOPMAL_CD
AND OIOP.ORDR_DLVY_INNB = SH.ORDR_DLVY_INNB
WHERE SH.SOPMAL_CD = #{sopmalCd}
AND SH.ORDR_INNB = #{ordrInnb}
AND SH.ORDR_DLVY_INNB = #{ordrDlvyInnb}
]]>
</select>
<!-- gc_goods_export 존재 여부 -->
<select id="selectGoodsExportCount"
parameterType="com.wb.domainserver.acidlivy.vo.AciDlivyOrderVO"
resultType="int">
<![CDATA[
SELECT COUNT(1)
FROM GC_GOODS_EXPORT
WHERE SOPMAL_CD = #{sopmalCd}
AND ORDR_INNB = #{ordrInnb}
AND ORDR_DLVY_INNB = #{ordrDlvyInnb}
AND HDRYCPN_CD = #{hdrycpnCd}
]]>
</select>
<!-- 기존 gc_goods_export 출고고유번호 조회 -->
<select id="selectGoodsExportDlivyInnb"
parameterType="com.wb.domainserver.acidlivy.vo.AciDlivyOrderVO"
resultType="java.lang.String">
<![CDATA[
SELECT DLIVY_INNB
FROM GC_GOODS_EXPORT
WHERE SOPMAL_CD = #{sopmalCd}
AND ORDR_INNB = #{ordrInnb}
AND ORDR_DLVY_INNB = #{ordrDlvyInnb}
AND HDRYCPN_CD = #{hdrycpnCd}
LIMIT 1
]]>
</select>
<!-- gc_goods_export INSERT -->
<!-- DLIVY_INNB: DB AUTO_INCREMENT 생성키 사용 -->
<!-- DLIVY_CD_NB: 생성키 수신 후 updateGoodsExportDlivyCdNb에서 설정 -->
<!-- HDRYCPN_CD: code22 고정, INVCNB: ACI BLNO -->
<insert id="insertGoodsExport"
parameterType="com.wb.domainserver.acidlivy.vo.AciDlivyOrderVO"
useGeneratedKeys="true"
keyProperty="dlivyInnb"
keyColumn="DLIVY_INNB">
<![CDATA[
INSERT INTO GC_GOODS_EXPORT (
SOPMAL_CD,
ORDR_INNB,
ORDR_DLVY_INNB,
DLIVY_STTUS_CD,
STTUS_UPDT_DT,
HDRYCPN_CD,
INVCNB,
DLIVY_DT,
DLIVY_COMPT_DT,
MNGR_INNB,
REGIST_DT,
UPDT_DT
) VALUES (
#{sopmalCd},
#{ordrInnb},
#{ordrDlvyInnb},
#{dlivySttusCd},
SYSDATE(),
#{hdrycpnCd},
#{blno},
SYSDATE(),
SYSDATE(),
0,
SYSDATE(),
SYSDATE()
)
]]>
</insert>
<!-- gc_goods_export DLIVY_CD_NB UPDATE -->
<!-- INSERT 경로에서 dlivyCdNb 생성 후 호출하여 DLIVY_CD_NB 설정 -->
<update id="updateGoodsExportDlivyCdNb"
parameterType="com.wb.domainserver.acidlivy.vo.AciDlivyOrderVO">
<![CDATA[
UPDATE GC_GOODS_EXPORT SET
DLIVY_CD_NB = #{dlivyCdNb},
UPDT_DT = SYSDATE()
WHERE DLIVY_INNB = #{dlivyInnb}
AND SOPMAL_CD = #{sopmalCd}
]]>
</update>
<!-- gc_goods_export UPDATE (기존 레코드 있음) -->
<update id="updateGoodsExport"
parameterType="com.wb.domainserver.acidlivy.vo.AciDlivyOrderVO">
<![CDATA[
UPDATE GC_GOODS_EXPORT SET
INVCNB = #{blno},
DLIVY_DT = SYSDATE(),
DLIVY_COMPT_DT = SYSDATE(),
UPDT_DT = SYSDATE()
WHERE SOPMAL_CD = #{sopmalCd}
AND ORDR_INNB = #{ordrInnb}
AND ORDR_DLVY_INNB = #{ordrDlvyInnb}
AND HDRYCPN_CD = #{hdrycpnCd}
]]>
</update>
<!-- gc_goods_export_item 존재 여부 -->
<select id="selectGoodsExportItemCount"
parameterType="dataMap"
resultType="int">
<![CDATA[
SELECT COUNT(1)
FROM GC_GOODS_EXPORT_ITEM
WHERE SOPMAL_CD = #{sopmalCd}
AND DLIVY_INNB = #{dlivyInnb}
AND ORDR_GOODS_OPTN_INNB = #{ordrGoodsOptnInnb}
AND OPTN_TY = #{optnTy}
]]>
</select>
<!-- gc_goods_export_item INSERT -->
<insert id="insertGoodsExportItem" parameterType="dataMap">
<selectKey order="BEFORE" keyProperty="dlivyGoodsInnb" resultType="java.lang.String">
SELECT IFNULL(MAX(DLIVY_GOODS_INNB), 0) + 1 FROM GC_GOODS_EXPORT_ITEM
</selectKey>
<![CDATA[
INSERT INTO GC_GOODS_EXPORT_ITEM (
DLIVY_GOODS_INNB,
DLIVY_INNB,
ORDR_GOODS_OPTN_INNB,
OPTN_TY,
SOPMAL_CD,
QY,
PYMNT_PREARNGE_QY,
ACCMLMNY_PYMNT_QY,
PYMNT_PREARNGE_EXTSH_QY,
NPAY_TRNSMIS_AT
) VALUES (
#{dlivyGoodsInnb},
#{dlivyInnb},
#{ordrGoodsOptnInnb},
#{optnTy},
#{sopmalCd},
#{qy},
#{qy},
0,
0,
NULL
)
]]>
</insert>
<!-- gc_goods_export_item UPDATE (기존 레코드 있음) -->
<update id="updateGoodsExportItem" parameterType="dataMap">
<![CDATA[
UPDATE GC_GOODS_EXPORT_ITEM SET
QY = (QY + #{qy}),
PYMNT_PREARNGE_QY = (QY + #{qy})
WHERE SOPMAL_CD = #{sopmalCd}
AND DLIVY_INNB = #{dlivyInnb}
AND ORDR_GOODS_OPTN_INNB = #{ordrGoodsOptnInnb}
]]>
</update>
<!-- gc_order_item_option 상태 UPDATE -->
<!-- ORDR_STTUS_CD: 부분출고(50) 또는 출고완료(55), STEP55 = 출고수량, STEP45 = 0 -->
<update id="updateOrderItemOptionStatus" parameterType="dataMap">
<![CDATA[
UPDATE GC_ORDER_ITEM_OPTION SET
ORDR_STTUS_CD = CASE
WHEN (ORDR_QY - STEP85) > #{qy} THEN '50'
WHEN (ORDR_QY - STEP85) = #{qy} THEN '55'
ELSE ORDR_STTUS_CD
END,
STEP55 = #{qy},
STEP45 = 0,
UPDT_DT = SYSDATE()
WHERE SOPMAL_CD = #{sopmalCd}
AND OPTN_TY = #{optnTy}
AND ORDR_GOODS_OPTN_INNB = #{ordrGoodsOptnInnb}
]]>
</update>
<!-- gc_order_shipping 상태 UPDATE -->
<!-- 하위의 모든 item_option 상태/수량 기준으로 동적 결정 (기존 updateBOOrdrDlvyStatusUpdt6575 참고) -->
<update id="updateOrderShippingStatus"
parameterType="com.wb.domainserver.acidlivy.vo.AciDlivyOrderVO">
<![CDATA[
UPDATE GC_ORDER_SHIPPING SH,
(
SELECT
CASE WHEN MAX(OIOP.ORDR_STTUS_CD) = MIN(OIOP.ORDR_STTUS_CD)
THEN MAX(OIOP.ORDR_STTUS_CD)
ELSE CAST(MAX(OIOP.ORDR_STTUS_CD) - 5 AS CHAR(2))
END AS ORDR_STTUS_CD,
SH.SOPMAL_CD,
SH.ORDR_INNB,
SH.ORDR_DLVY_INNB
FROM GC_ORDER_SHIPPING SH
INNER JOIN GC_ORDER_ITEM_OPTION OIOP
ON SH.SOPMAL_CD = OIOP.SOPMAL_CD
AND SH.ORDR_DLVY_INNB = OIOP.ORDR_DLVY_INNB
AND OIOP.ORDR_QY <> OIOP.STEP85
WHERE SH.SOPMAL_CD = #{sopmalCd}
AND SH.ORDR_INNB = #{ordrInnb}
AND SH.ORDR_DLVY_INNB = #{ordrDlvyInnb}
AND SH.USE_AT = 'Y'
GROUP BY SH.SOPMAL_CD, SH.ORDR_INNB, SH.ORDR_DLVY_INNB
) STEP
SET
SH.ORDR_STTUS_CD = STEP.ORDR_STTUS_CD,
SH.UPDT_DT = SYSDATE()
WHERE SH.SOPMAL_CD = STEP.SOPMAL_CD
AND SH.ORDR_INNB = STEP.ORDR_INNB
AND SH.ORDR_DLVY_INNB = STEP.ORDR_DLVY_INNB
]]>
</update>
<!-- gc_order 상태 UPDATE -->
<!-- 하위의 모든 shipping 상태 기준으로 동적 결정 (기존 updateBOOrdrStatusUpdt6575 참고) -->
<update id="updateOrderStatus"
parameterType="com.wb.domainserver.acidlivy.vo.AciDlivyOrderVO">
<![CDATA[
UPDATE GC_ORDER O,
(
SELECT
CASE WHEN MAX(SH.ORDR_STTUS_CD) = MIN(SH.ORDR_STTUS_CD)
THEN MAX(SH.ORDR_STTUS_CD)
ELSE CAST(MAX(SH.ORDR_STTUS_CD) - 5 AS CHAR(2))
END AS ORDR_STTUS_CD,
O.SOPMAL_CD,
O.ORDR_INNB
FROM GC_ORDER O
INNER JOIN GC_ORDER_SHIPPING SH
ON O.SOPMAL_CD = SH.SOPMAL_CD
AND O.ORDR_INNB = SH.ORDR_INNB
AND SH.ORDR_STTUS_CD <> '85'
WHERE O.SOPMAL_CD = #{sopmalCd}
AND O.ORDR_INNB = #{ordrInnb}
GROUP BY O.SOPMAL_CD, O.ORDR_INNB
) STEP
SET
O.ORDR_STTUS_CD = STEP.ORDR_STTUS_CD,
O.UPDT_DT = SYSDATE()
WHERE O.SOPMAL_CD = STEP.SOPMAL_CD
AND O.ORDR_INNB = STEP.ORDR_INNB
AND O.USE_AT = 'Y'
]]>
</update>
<!-- GC_GOODS_EXPORT_LOG INSERT -->
<insert id="insertGoodsExportLog" parameterType="dataMap">
<selectKey order="BEFORE" keyProperty="dlivyLogInnb" resultType="java.lang.String">
SELECT IFNULL(MAX(DLIVY_LOG_INNB), 0) + 1 FROM GC_GOODS_EXPORT_LOG
</selectKey>
<![CDATA[
INSERT INTO GC_GOODS_EXPORT_LOG (
DLIVY_LOG_INNB,
ORDR_INNB,
ORDR_DLVY_INNB,
MNGR_INNB,
DLIVY_CD_NB,
DLIVY_GOODS_CD_NB,
INVNTRY_SETUP_CD,
REQST_PROCS_STTUS_CD,
MSSAGE_CN,
REGIST_DT
) SELECT
#{dlivyLogInnb},
#{ordrInnb},
#{ordrDlvyInnb},
#{regusr},
DLIVY_CD_NB,
#{dlivyGoodsCdNb},
#{invntrySetupCd},
#{reqstProcsSttusCd},
#{mssageCn},
SYSDATE()
FROM GC_GOODS_EXPORT
WHERE SOPMAL_CD = #{sopmalCd}
AND DLIVY_INNB = #{dlivyInnb}
]]>
</insert>
<!-- GC_ORDER_LOG INSERT -->
<insert id="insertOrderLog" parameterType="dataMap">
<![CDATA[
INSERT INTO GC_ORDER_LOG (
ORDR_INNB,
SOPMAL_CD,
LOG_SJ,
LOG_DTL_CN,
ADIT_INFO_CN,
DLIVY_INNB,
ORDER_REFUND_INNB,
LOG_DV_CD,
DV_CD,
OPETR_NM,
QY,
AMT,
PROCS_DT
) VALUES (
#{ordrInnb},
#{sopmalCd},
#{logSj},
#{logDtlCn},
#{aditInfoCn},
#{dlivyInnb},
#{refundCdNb},
#{logDvCd},
#{dvCd},
#{opetrNm},
#{qy},
#{amt},
SYSDATE()
)
]]>
</insert>
<!-- 배송처리 대상 주문 목록 조회 (DLIVY_STTUS_CD IN ('55', '65')) -->
<select id="selectGoodsExportList" resultType="com.wb.domainserver.acidlivy.vo.AciDlivyOrderVO">
<![CDATA[
SELECT
DLIVY_INNB,
ORDR_INNB,
ORDR_DLVY_INNB,
SOPMAL_CD,
INVCNB,
DLIVY_STTUS_CD
FROM GC_GOODS_EXPORT
WHERE DLIVY_STTUS_CD IN ('55', '65')
ORDER BY DLIVY_INNB ASC
]]>
</select>
<!-- 출고 상태 일괄 변경 다건 -->
<update id="updateBODlivySttusGetbackUpdtArr" parameterType="com.wb.domainserver.acidlivy.vo.BODlivyUpdtVO">
<![CDATA[
UPDATE GC_GOODS_EXPORT SET
DLIVY_STTUS_CD = #{dlivySttusCd},
]]>
<if test="dlivySttusCd == '75'">
<![CDATA[
DLVY_COMPT_DT = SYSDATE(),
]]>
</if>
<![CDATA[
STTUS_UPDT_DT = SYSDATE(),
MNGR_INNB = #{mngrInnb},
UPDT_DT = SYSDATE() -- TODO : 상태 변경일시, 구매확정일시를 수정하는데 수정일시도 수정?
WHERE SOPMAL_CD = #{sopmalCd}
AND DLIVY_INNB = #{dlivyInnb}
]]>
</update>
<!-- 출고완료상태의 출고상품 목록 조회 -->
<select id="selectBOExportOrderItemInqire">
<![CDATA[
SELECT
EXI.ORDR_GOODS_OPTN_INNB
,EXI.OPTN_TY
,EXI.SOPMAL_CD
,(OIOP.STEP45 + OIOP.STEP55 + OIOP.STEP65 + OIOP.STEP75) AS QY
,0 AS PYMNT_PREARNGE_QY -- 지급예정수량
,IFNULL((EXI.PYMNT_PREARNGE_QY - OIOP.STEP85), 0) AS ACCMLMNY_PYMNT_QY -- 적립금지급수량
,IFNULL(OIOP.STEP85, 0) AS PYMNT_PREARNGE_EXTSH_QY -- 지급예정소멸수량
,EX.ORDR_INNB
,O.MBR_DV_CD
,O.MBR_INNB
,OIOP.ORDR_DLVY_INNB
,IFNULL(EX.DLIVY_CD_NB, '') AS DLIVY_CD_NB
,EX.DLIVY_INNB
,IFNULL(OIOP.MLG_VALID_PD, 0) AS MLG_VALID_PD
,IFNULL(OIOP.MLG_VALID_PD_AT, 'N') AS MLG_VALID_PD_AT
,CASE
WHEN OIOP.STEP85 = 0 THEN ROUND(OIOP.MLG_ACCML_UNIT * ( OIOP.ORDR_QY - OIOP.STEP85 ) + OIOP.MLG_ACCML_REST)
WHEN OIOP.STEP85 > 0 THEN ROUND(OIOP.MLG_ACCML_UNIT * ( OIOP.ORDR_QY - OIOP.STEP85 ))
END AS MLG_ACCML_AMT
FROM GC_GOODS_EXPORT_ITEM EXI
INNER JOIN GC_GOODS_EXPORT EX ON EXI.SOPMAL_CD = EX.SOPMAL_CD
AND EXI.DLIVY_INNB = EX.DLIVY_INNB
INNER JOIN GC_ORDER_ITEM_OPTION OIOP ON EXI.SOPMAL_CD = OIOP.SOPMAL_CD
AND EXI.OPTN_TY = OIOP.OPTN_TY
AND EXI.ORDR_GOODS_OPTN_INNB = OIOP.ORDR_GOODS_OPTN_INNB
AND OIOP.PURCHS_DCSN_QY < ( OIOP.ORDR_QY - OIOP.STEP85 )
INNER JOIN GC_ORDER O ON EX.SOPMAL_CD = O.SOPMAL_CD
AND EX.ORDR_INNB = O.ORDR_INNB
WHERE 1 = 1
AND EX.SOPMAL_CD = #{sopmalCd}
]]>
<choose>
<when test="@com.wb.domainserver.util.dataaccess.MybaitsUtil@isNotEmpty(dlivyInnb)">
<![CDATA[
AND EX.DLIVY_INNB = #{dlivyInnb}
]]>
</when>
<when test="@com.wb.domainserver.util.dataaccess.MybaitsUtil@isNotEmpty(ordrGoodsOptnInnb)">
<![CDATA[
AND EXI.OPTN_TY = #{optnTy}
AND EXI.ORDR_GOODS_OPTN_INNB = #{ordrGoodsOptnInnb}
]]>
</when>
<when test="@com.wb.domainserver.util.dataaccess.MybaitsUtil@isNotEmpty(ordrInnb)">
<![CDATA[
AND EX.ORDR_INNB = #{ordrInnb}
]]>
</when>
</choose>
</select>
<!-- 출고 처리 후 주문 상품 옵션 상태 변경 -->
<update id="updateBOOrdrItemStatusUpdt6575">
<![CDATA[
UPDATE GC_ORDER_ITEM_OPTION SET
]]>
<choose>
<when test="@com.wb.domainserver.util.dataaccess.MybaitsUtil@isEquals(dlivySttusCd, '65')">
<![CDATA[
ORDR_STTUS_CD =
CASE
WHEN (ORDR_QY - STEP85) > #{qy} THEN '60'
WHEN (ORDR_QY - STEP85) = #{qy} THEN '65'
ELSE ORDR_STTUS_CD
END
,STEP65 = #{qy}
,STEP55 = CASE
WHEN (STEP55 - #{qy}) < 0 THEN 0
ELSE (STEP55 - #{qy})
END,
]]>
</when>
<when test="@com.wb.domainserver.util.dataaccess.MybaitsUtil@isEquals(dlivySttusCd, '75')">
<![CDATA[
ORDR_STTUS_CD =
CASE
WHEN (ORDR_QY - STEP85) > #{qy} THEN '70'
WHEN (ORDR_QY - STEP85) = #{qy} THEN '75'
ELSE ORDR_STTUS_CD
END
,STEP55 = CASE
WHEN (STEP55 - #{qy}) < 0 THEN 0
ELSE (STEP55 - #{qy})
END
,STEP65 = CASE
WHEN (STEP65 - #{qy}) < 0 THEN 0
ELSE (STEP65 - #{qy})
END
,STEP75 = #{qy},
]]>
</when>
<when test="@com.wb.domainserver.util.dataaccess.MybaitsUtil@isEquals(dlivySttusCd, '55')">
<![CDATA[
ORDR_STTUS_CD =
CASE
WHEN (ORDR_QY - STEP85) > #{qy} THEN '50'
WHEN (ORDR_QY - STEP85) = #{qy} THEN '55'
ELSE ORDR_STTUS_CD
END
,STEP55 = #{qy},
]]>
</when>
</choose>
<![CDATA[
STEP45 = 0
,UPDT_DT = SYSDATE()
WHERE SOPMAL_CD = #{sopmalCd}
AND OPTN_TY = #{optnTy}
AND ORDR_GOODS_OPTN_INNB = #{ordrGoodsOptnInnb}
]]>
</update>
<!-- 출고 처리 후 주문 배송 상태 변경
SH.ORDR_STTUS_CD =
CASE
WHEN EXISTS (
SELECT
1
FROM GC_ORDER_ITEM_OPTION OIOP
WHERE OIOP.SOPMAL_CD = SH.SOPMAL_CD
AND OIOP.ORDR_DLVY_INNB = SH.ORDR_DLVY_INNB
AND OIOP.ORDR_STTUS_CD = '60'
) THEN '60'
WHEN EXISTS (
SELECT
1
FROM GC_ORDER_ITEM_OPTION OIOP
WHERE OIOP.SOPMAL_CD = SH.SOPMAL_CD
AND OIOP.ORDR_DLVY_INNB = SH.ORDR_DLVY_INNB
AND OIOP.ORDR_STTUS_CD = '65'
) THEN '65'
ELSE ORDR_STTUS_CD
END
-->
<update id="updateBOOrdrDlvyStatusUpdt6575">
<![CDATA[
UPDATE
GC_ORDER_SHIPPING SH,
(
SELECT CASE WHEN MAX(OIOP.ORDR_STTUS_CD) = MIN(OIOP.ORDR_STTUS_CD) THEN MAX(OIOP.ORDR_STTUS_CD)
ELSE CAST(MAX(OIOP.ORDR_STTUS_CD) - 5 AS CHAR(2))
END ORDR_STTUS_CD
, SH.SOPMAL_CD, SH.ORDR_INNB, SH.ORDR_DLVY_INNB
FROM GC_ORDER_SHIPPING SH
INNER JOIN GC_ORDER_ITEM_OPTION OIOP
ON SH.SOPMAL_CD = OIOP.SOPMAL_CD
AND SH.ORDR_DLVY_INNB = OIOP.ORDR_DLVY_INNB
AND OIOP.ORDR_QY <> OIOP.STEP85
WHERE SH.SOPMAL_CD = #{sopmalCd}
AND SH.ORDR_INNB = #{ordrInnb}
]]>
<choose>
<when test="@com.wb.domainserver.util.dataaccess.MybaitsUtil@isNotEmpty(ordrDlvyInnb)">
AND SH.ORDR_DLVY_INNB = #{ordrDlvyInnb}
</when>
<when test="@com.wb.domainserver.util.dataaccess.MybaitsUtil@isNotEmpty(ordrDlvyInnbArr)">
<![CDATA[
AND SH.ORDR_DLVY_INNB IN
]]>
<foreach collection="ordrDlvyInnbArr" item="ordrDlvyInnbArr" open="(" close=")" separator=",">
#{ordrDlvyInnbArr}
</foreach>
</when>
</choose>
<![CDATA[
AND SH.USE_AT = 'Y'
GROUP BY SH.SOPMAL_CD, SH.ORDR_INNB, SH.ORDR_DLVY_INNB
]]>
<![CDATA[
) STEP
SET
SH.ORDR_STTUS_CD = STEP.ORDR_STTUS_CD
,SH.MNGR_INNB = #{updusr}
,SH.UPDT_DT = SYSDATE()
WHERE 1 = 1
AND SH.SOPMAL_CD = STEP.SOPMAL_CD
AND SH.ORDR_INNB = STEP.ORDR_INNB
AND SH.ORDR_DLVY_INNB = STEP.ORDR_DLVY_INNB
]]>
</update>
<!-- 출고 처리 후 주문 상태 변경
UPDATE GC_ORDER O SET
O.ORDR_STTUS_CD =
CASE
WHEN EXISTS (
SELECT
1
FROM GC_ORDER_SHIPPING SH
WHERE O.SOPMAL_CD = SH.SOPMAL_CD
AND O.ORDR_INNB = SH.ORDR_INNB
AND SH.ORDR_STTUS_CD = '60'
) THEN '60'
WHEN EXISTS (
SELECT
1
FROM GC_ORDER_SHIPPING SH
WHERE O.SOPMAL_CD = SH.SOPMAL_CD
AND O.ORDR_INNB = SH.ORDR_INNB
AND SH.ORDR_STTUS_CD = '65'
) THEN '65'
ELSE ORDR_STTUS_CD
END
WHERE O.SOPMAL_CD = #{sopmalCd}
AND O.ORDR_INNB = #{ordrInnb}
AND O.USE_AT = 'Y'
-->
<update id="updateBOOrdrStatusUpdt6575">
<![CDATA[
UPDATE
GC_ORDER O,
(SELECT CASE WHEN MAX(SH.ORDR_STTUS_CD) = MIN(SH.ORDR_STTUS_CD) THEN MAX(SH.ORDR_STTUS_CD)
ELSE CAST(MAX(SH.ORDR_STTUS_CD) - 5 AS CHAR(2))
END ORDR_STTUS_CD
, O.SOPMAL_CD, O.ORDR_INNB
FROM GC_ORDER O
INNER JOIN GC_ORDER_SHIPPING SH
ON O.SOPMAL_CD = SH.SOPMAL_CD
AND O.ORDR_INNB = SH.ORDR_INNB
AND SH.ORDR_STTUS_CD <> '85'
WHERE O.SOPMAL_CD = #{sopmalCd}
AND O.ORDR_INNB = #{ordrInnb}
GROUP BY O.SOPMAL_CD, O.ORDR_INNB
) STEP
SET
O.ORDR_STTUS_CD = STEP.ORDR_STTUS_CD
,O.MNGR_INNB = #{updusr}
,O.UPDT_DT = SYSDATE()
WHERE 1 = 1
AND O.SOPMAL_CD = STEP.SOPMAL_CD
AND O.ORDR_INNB = STEP.ORDR_INNB
AND O.USE_AT = 'Y'
]]>
</update>
</mapper>