passivityCircuitAllocationRef (useRef) 선언 제거 원인: function component에 ref 전달 시 React warning 발생 (forwardRef 미사용) PCS 접속함 응답에 추가된 moduleTpCd 필드를 Cross Mix 식별을 위해 프론트 상태 보존 및 getQuotationItem 제출 파라미터까지 흘려보내고, dedupe 키를 (itemId, moduleTpCd) 조합으로 변경.
394 lines
14 KiB
JavaScript
394 lines
14 KiB
JavaScript
import { useAxios } from '@/hooks/useAxios'
|
|
import { useMessage } from '@/hooks/useMessage'
|
|
import { useSwal } from '@/hooks/useSwal'
|
|
import { sessionStore } from '@/store/commonAtom'
|
|
import { getQueryString } from '@/util/common-utils'
|
|
import { atom, useRecoilState, useRecoilValue } from 'recoil'
|
|
|
|
|
|
// API 요청을 저장할 모듈 레벨 변수 (Hook 외부)
|
|
// 전역 Promise 캐시
|
|
const roofMaterialPromiseCache = new Map()
|
|
let cacheKey = 'default'
|
|
|
|
/**
|
|
* 마스터 컨트롤러 훅
|
|
* @returns
|
|
*/
|
|
export function useMasterController() {
|
|
const { get, post, put } = useAxios()
|
|
const { getMessage } = useMessage()
|
|
const { swalFire } = useSwal()
|
|
const sessionState = useRecoilValue(sessionStore)
|
|
|
|
/**
|
|
* 지붕재 목록 조회
|
|
* @returns
|
|
*/
|
|
|
|
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
|
|
|
|
const getRoofMaterialList = async () => {
|
|
// 1. 이미 진행 중이거나 완료된 Promise가 있으면 그것을 반환
|
|
if (roofMaterialPromiseCache.has(cacheKey)) {
|
|
console.log('🔄 getRoofMaterialList: Returning cached promise')
|
|
return roofMaterialPromiseCache.get(cacheKey)
|
|
}
|
|
|
|
console.log('🚀 getRoofMaterialList: Creating new promise')
|
|
|
|
const MAX_RETRY = 15 // 15번으로 증가 (일본 지역 대응)
|
|
const RETRY_DELAY = 2000 // 2초 간격
|
|
const MAX_BACKOFF = 10000 // 최대 10초까지 지수 백오프
|
|
|
|
const fetchWithRetry = async () => {
|
|
let lastError = null
|
|
let retryCount = 0
|
|
|
|
for (let attempt = 0; attempt < MAX_RETRY; attempt++) {
|
|
try {
|
|
// 지수 백오프 적용: 2초, 4초, 8초, ... 최대 10초
|
|
const backoffDelay = Math.min(RETRY_DELAY * Math.pow(2, attempt), MAX_BACKOFF)
|
|
if (attempt > 0) {
|
|
console.log(`⏳ Waiting ${backoffDelay/1000}s before retry ${attempt + 1}/${MAX_RETRY}`)
|
|
await delay(backoffDelay)
|
|
}
|
|
|
|
console.log(`🌐 API Request ${attempt + 1}/${MAX_RETRY}: /api/v1/master/getRoofMaterialList`)
|
|
const res = await get({ url: '/api/v1/master/getRoofMaterialList' })
|
|
console.log('✅ getRoofMaterialList: Success on attempt', attempt + 1)
|
|
|
|
// 재시도 중이었으면 성공 메시지 표시
|
|
if (retryCount > 0) {
|
|
swalFire({
|
|
icon: 'success',
|
|
title: getMessage('server.restored.title'),
|
|
text: `${getMessage('server.restored.text')} (${retryCount}${getMessage('server.retries')})`,
|
|
timer: 3000, // 메시지 표시 시간 증가
|
|
showConfirmButton: false
|
|
})
|
|
}
|
|
|
|
return res
|
|
} catch (error) {
|
|
lastError = error
|
|
retryCount++
|
|
|
|
// 500 계열만 기다렸다가 재시도
|
|
const status = error?.response?.status
|
|
if (status && status >= 500) {
|
|
console.log(`⚠️ getRoofMaterialList: 500 error on attempt ${attempt + 1}, retrying...`)
|
|
|
|
// 첫 번째 500 에러에만 메시지 표시
|
|
if (attempt === 0) {
|
|
swalFire({
|
|
icon: 'warning',
|
|
title: getMessage('server.error.title'),
|
|
text: getMessage('server.error.text'),
|
|
timer: 4000, // 경고 메시지 시간 증가
|
|
showConfirmButton: false
|
|
})
|
|
}
|
|
|
|
continue
|
|
}
|
|
|
|
// 네트워크 에러(ECONNRESET, ETIMEDOUT 등)도 재시도
|
|
if (error.code === 'ECONNRESET' || error.code === 'ETIMEDOUT' || error.code === 'ENOTFOUND') {
|
|
console.log(`🌐 Network error on attempt ${attempt + 1}:`, error.code)
|
|
if (attempt === 0) {
|
|
swalFire({
|
|
icon: 'warning',
|
|
title: getMessage('network.error.title'),
|
|
text: getMessage('network.error.text'),
|
|
timer: 4000,
|
|
showConfirmButton: false
|
|
})
|
|
}
|
|
continue
|
|
}
|
|
|
|
// 500이나 네트워크 에러가 아니면 즉시 실패
|
|
console.log(`❌ getRoofMaterialList: Non-retryable error on attempt ${attempt + 1}:`, error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
// 모든 재시도 실패 시
|
|
console.log('💥 getRoofMaterialList: All retries failed')
|
|
swalFire({
|
|
icon: 'error',
|
|
title: getMessage('server.failed.title'),
|
|
text: `${getMessage('server.failed.text')} (${MAX_RETRY}${getMessage('server.retries')})`,
|
|
confirmButtonText: getMessage('common.ok')
|
|
})
|
|
|
|
throw lastError
|
|
}
|
|
|
|
const promise = fetchWithRetry()
|
|
.finally(() => {
|
|
// 성공/실패 모두 끝나면 캐시에서 제거
|
|
console.log('🏁 getRoofMaterialList: Promise resolved, removing from cache')
|
|
roofMaterialPromiseCache.delete(cacheKey)
|
|
})
|
|
|
|
// 캐시에 Promise 저장
|
|
roofMaterialPromiseCache.set(cacheKey, promise)
|
|
return promise
|
|
}
|
|
|
|
/**
|
|
* 모듈 타입별 아이템 목록 조회
|
|
* @param {지붕재 코드 목록} arrRoofMatlCd
|
|
* @returns
|
|
*/
|
|
const getModuleTypeItemList = async (paramArr) => {
|
|
if (!Array.isArray(paramArr) || paramArr.length === 0 || paramArr.length > 4 || paramArr.some((item) => !item || item.trim() === '')) {
|
|
swalFire({ text: getMessage('master.moduletypeitem.message.error'), type: 'alert', icon: 'error' })
|
|
return null
|
|
}
|
|
const paramString = `?${paramArr.map((item) => `arrRoofMatlCd=${item}`).join('&')}`
|
|
return await get({ url: `/api/v1/master/getModuleTypeItemList${paramString}&storeId=${sessionState.oneTwoStoreId}` }).then((res) => {
|
|
// console.log('🚀🚀 ~ getModuleTypeItemList ~ res:', res)
|
|
return res
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 가대 목록 조회
|
|
* @param {모듈타입코드} moduleTpCd
|
|
* @param {지붕재코드} roofMatlCd
|
|
* @param {서까래기초코드} raftBaseCd
|
|
* @param {가대메이커코드} trestleMkrCd
|
|
* @param {공법코드} constMthdCd
|
|
* @param {지붕기초코드} roofBaseCd
|
|
* @returns
|
|
*/
|
|
const getTrestleList = async (params) => {
|
|
const paramString = getQueryString(params)
|
|
// console.log('🚀🚀 ~ getTrestleList ~ paramString:', paramString)
|
|
return await get({ url: '/api/v1/master/getTrestleList' + paramString }).then((res) => {
|
|
// console.log('🚀🚀 ~ getTrestleList ~ res:', res)
|
|
return res
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 모듈 시공법 목록 조회
|
|
* @param {모듈타입코드} moduleTpCd
|
|
* @param {지붕재코드} roofMatlCd
|
|
* @param {가대메이커코드} trestleMkrCd
|
|
* @param {공법코드} constMthdCd
|
|
* @param {지붕기초코드} roofBaseCd
|
|
* @param {면조도} illuminationTp
|
|
* @param {설치높이} instHt
|
|
* @param {풍속} stdWindSpeed
|
|
* @param {적설량} stdSnowLd
|
|
* @param {경사도코드} inclCd
|
|
* @param {서까래기초코드} raftBaseCd
|
|
* @param {하제(망둥어)피치} roofPitch
|
|
*
|
|
* @returns
|
|
*/
|
|
const getConstructionList = async (params) => {
|
|
const paramString = getQueryString(params)
|
|
// console.log('🚀🚀 ~ getConstructionList ~ paramString:', paramString)
|
|
return await get({ url: '/api/v1/master/getConstructionList' + paramString }).then((res) => {
|
|
// console.log('🚀🚀 ~ getConstructionList ~ res:', res)
|
|
return res
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 가대 상세 조회
|
|
* @param {모듈타입코드} moduleTpCd
|
|
* @param {지붕재코드} roofMatlCd
|
|
* @param {가대메이커코드} trestleMkrCd
|
|
* @param {공법코드} constMthdCd
|
|
* @param {지붕기초코드} roofBaseCd
|
|
* @param {면조도} illuminationTp
|
|
* @param {설치높이} instHt
|
|
* @param {풍속} stdWindSpeed
|
|
* @param {적설량} stdSnowLd
|
|
* @param {경사도코드} inclCd
|
|
* @param {시공법} constTp
|
|
* @param {혼합모듈번호} mixMatlNo
|
|
* @param {하제(망둥어)피치}roofPitch
|
|
* @returns
|
|
*/
|
|
const getTrestleDetailList = async (params) => {
|
|
// const paramString = getQueryString(params)
|
|
return await post({ url: '/api/v1/master/getTrestleDetailList', data: params }).then((res) => {
|
|
return res
|
|
})
|
|
}
|
|
|
|
/**
|
|
* PCS 메이커, 시리즈 목록 조회
|
|
* @param {PCS 메이커코드} pcsMkrCd
|
|
* @param {혼합모듈번호} mixMatlNo
|
|
* @returns
|
|
*/
|
|
const getPcsMakerList = async (params = null) => {
|
|
let paramString = ''
|
|
if (params) {
|
|
paramString = getQueryString(params)
|
|
}
|
|
return await get({ url: '/api/v1/master/pcsMakerList' + paramString }).then((res) => {
|
|
return res
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 모듈 타입별 아이템 목록 조회
|
|
* @param {PCS 메이커코드} pcsMkrCd
|
|
* @param {PCS시리즈코드 목록} pcsSerList
|
|
* @param {모듈아이템 ID 목록} moduleItemList
|
|
* @returns
|
|
*/
|
|
const getPcsModelList = async (params = null) => {
|
|
return await post({ url: '/api/v1/master/getPcsSeriesItemList', data: params }).then((res) => {
|
|
return res
|
|
})
|
|
}
|
|
/**
|
|
* 시리즈중 자동으로 추천 PCS 정보 조회
|
|
* @param {Max접속(과적)여부} maxConnYn
|
|
* @param {동일회로도여부} smpCirYn
|
|
* @param {한랭지여부} coldZoneYn
|
|
* @param {사용된 모듈 아이템 List} useModuleItemList
|
|
* @param {지붕면별 목록} roofSurfaceList
|
|
* @param {PCS 제품 목록} pcsItemList
|
|
* @returns
|
|
*/
|
|
const getPcsAutoRecommendList = async (params = null) => {
|
|
return await post({ url: '/api/v1/master/getPcsAutoRecommendList', data: params }).then((res) => {
|
|
console.log('[moduleTpCd] getPcsAutoRecommendList req:', params)
|
|
console.log('[moduleTpCd] getPcsAutoRecommendList res pcsItemList:', res?.data?.pcsItemList?.map((p) => ({ itemId: p.itemId, pcsTpCd: p.pcsTpCd, connList: p.connList?.map((c) => ({ itemId: c.itemId, moduleTpCd: c.moduleTpCd })) })))
|
|
return res
|
|
})
|
|
}
|
|
|
|
const getPcsManualConfChk = async (params = null) => {
|
|
return await post({ url: '/api/v1/master/getPcsMenualConfChk', data: params }).then((res) => {
|
|
return res
|
|
})
|
|
}
|
|
|
|
/**
|
|
* PCS로 회로 구성 가능 여부 체크
|
|
* @param {Max접속(과적)여부} maxConnYn
|
|
* @param {동일회로도여부} smpCirYn
|
|
* @param {한랭지여부} coldZoneYn
|
|
* @param {사용된 모듈 아이템 List} useModuleItemList
|
|
* @param {지붕면별 목록} roofSurfaceList
|
|
* @param {PCS 제품 목록} pcsItemList
|
|
* @returns
|
|
*/
|
|
const getPcsVoltageChk = async (params = null) => {
|
|
return await post({ url: '/api/v1/master/getPcsVoltageChk', data: params }).then((res) => {
|
|
console.log('[moduleTpCd] getPcsVoltageChk req:', params)
|
|
console.log('[moduleTpCd] getPcsVoltageChk res pcsItemList:', res?.data?.pcsItemList?.map((p) => ({ itemId: p.itemId, pcsTpCd: p.pcsTpCd, connList: p.connList?.map((c) => ({ itemId: c.itemId, moduleTpCd: c.moduleTpCd })) })))
|
|
return res
|
|
})
|
|
}
|
|
|
|
/**
|
|
* PCS 승압설정 정보 조회
|
|
* @param {Max접속(과적)여부} maxConnYn
|
|
* @param {동일회로도여부} smpCirYn
|
|
* @param {한랭지여부} coldZoneYn
|
|
* @param {사용된 모듈아이템 목록} useModuleItemList
|
|
* @param {지붕면 목록} roofSurfaceList
|
|
* @param {PCS 아이템 목록} pcsItemList
|
|
|
|
* @returns
|
|
*/
|
|
const getPcsVoltageStepUpList = async (params2 = null) => {
|
|
// roofSurfaceList의 moduleList에서 circuit 속성을 제외
|
|
const modifiedRoofSurfaceList = params2.roofSurfaceList.map((surface) => ({
|
|
...surface,
|
|
moduleList: surface.moduleList.map((module) => ({
|
|
itemId: module.itemId,
|
|
uniqueId: module.uniqueId,
|
|
})),
|
|
}))
|
|
|
|
const params = {
|
|
...params2,
|
|
maxConnYn: params2.maxConnYn,
|
|
smpCirYn: params2.smpCirYn,
|
|
coldZoneYn: params2.coldZoneYn,
|
|
useModuleItemList: params2.useModuleItemList,
|
|
roofSurfaceList: modifiedRoofSurfaceList,
|
|
pcsItemList: params2.pcsItemList,
|
|
}
|
|
|
|
return await post({ url: '/api/v1/master/getPcsVoltageStepUpList', data: params }).then((res) => {
|
|
console.log('[moduleTpCd] getPcsVoltageStepUpList req:', params)
|
|
console.log('[moduleTpCd] getPcsVoltageStepUpList res pcsItemList:', res?.data?.pcsItemList?.map((p) => ({ itemId: p.itemId, pcsTpCd: p.pcsTpCd, connList: p.connList?.map((c) => ({ itemId: c.itemId, moduleTpCd: c.moduleTpCd })) })))
|
|
return res
|
|
})
|
|
}
|
|
|
|
const getQuotationItem = async (params) => {
|
|
console.log('[moduleTpCd] getQuotationItem req pcses.connections:', params?.pcses?.map((p) => ({ pcsItemId: p.pcsItemId, connections: p.connections })))
|
|
console.log('[moduleTpCd] getQuotationItem full req:', params)
|
|
return await post({ url: '/api/v1/master/getQuotationItem', data: params })
|
|
.then((res) => {
|
|
return res
|
|
})
|
|
.catch((error) => {
|
|
console.log('🚀 ~ getQuotationItem ~ error:', error)
|
|
setIsGlobalLoading(false)
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 물건 정보 업데이트 api 호출
|
|
* @param {objectNo standardWindSpeedId verticalSnowCover surfaceType installHeight userId} params
|
|
* @param {물건번호 기준풍속코드 적설량 면조도구분 설치높이 작성자아아디 } params
|
|
* @returns
|
|
*/
|
|
const updateObjectDate = async (params = null) => {
|
|
return await put({ url: '/api/object/update-object-date', data: params })
|
|
}
|
|
|
|
/**
|
|
* PCS 접속함 및 옵션 목록 조회
|
|
* @param {사용된 모듈 아이템 목록} useModuleItemList
|
|
* @param {PCS 제품 목록} pcsItemList
|
|
* @returns
|
|
*/
|
|
const getPcsConnOptionItemList = async (params = null) => {
|
|
return await post({ url: '/api/v1/master/getPcsConnOptionItemList', data: params }).then((res) => {
|
|
console.log('[moduleTpCd] getPcsConnOptionItemList req:', params)
|
|
console.log('[moduleTpCd] getPcsConnOptionItemList res pcsItemList:', res?.data?.pcsItemList?.map((p) => ({ itemId: p.itemId, pcsTpCd: p.pcsTpCd, connList: p.connList?.map((c) => ({ itemId: c.itemId, moduleTpCd: c.moduleTpCd })) })))
|
|
return res
|
|
})
|
|
}
|
|
|
|
const quotationErrSave = async (params) => {
|
|
return await post({ url: '/api/v1/master/quotationErrSave', data: params })
|
|
}
|
|
|
|
return {
|
|
getRoofMaterialList,
|
|
getModuleTypeItemList,
|
|
getTrestleList,
|
|
getConstructionList,
|
|
getTrestleDetailList,
|
|
getPcsMakerList,
|
|
getPcsModelList,
|
|
getPcsAutoRecommendList,
|
|
getPcsVoltageChk,
|
|
getPcsManualConfChk,
|
|
getPcsVoltageStepUpList,
|
|
updateObjectDate,
|
|
getQuotationItem,
|
|
getPcsConnOptionItemList,
|
|
quotationErrSave,
|
|
}
|
|
}
|