rdii, 오수발생량, 지하수유입량 추정 속도 및 다운로드 개선

This commit is contained in:
2025-10-27 15:03:50 +09:00
parent 57a4fa54eb
commit 4dc31882a6
7 changed files with 163 additions and 125 deletions
@@ -145,71 +145,74 @@ export default function DashBoard() {
const excelDownload = () => {
if (!fcltyList.bscFctCd) return;
switch (activeTab) {
case 'facilityInfo':
searchOperationStatus(false, searchYear, fcltyList.bscFctCd);
searchOperationStatus(false, searchYear, fcltyList.bscFctCd).then(() => {setTimeout(() => {makeExcelData();}, 200);});
fetch(`/mainDashBoard.xlsx`, {}).then(async (response) => {
if (!response.ok) {
throw Error(response.statusText);
}
// 받은 파일 바인너리 처리
const blobRet = await response.arrayBuffer();
const excelData1 = planFcltyList.map((item) => {
return [
item.item,
Number(item.provisionalApproval.toString().replaceAll(',',''))?Number(item.provisionalApproval.toString().replaceAll(',','')):item.provisionalApproval,
item.now,
item.stepOne,
item.stepTwo,
item.stepThree,
item.stepFour,
];
});
// operationalStatusList를 2차원 배열로 변환
const excelData2 = operationalStatusList.map((item, index) => {
const baseData = [
Number(item.yearAvg?.replaceAll(',','')),
Number(item.yearMax?.replaceAll(',','')),
Number(item.yearMin?.replaceAll(',','')),
Number(item.summerAll?.replaceAll(',','')),
Number(item.summerThree?.replaceAll(',','')),
Number(item.summerRain?.replaceAll(',','')),
Number(item.winterAll?.replaceAll(',','')),
Number(item.winterSunnyAvg?.replaceAll(',','')),
Number(item.winterSunnyMax?.replaceAll(',','')),
Number(item.winterSunnyMin?.replaceAll(',','')),
Number(item.groundWaterAvg?.replaceAll(',','').replaceAll('%','')),
Number(item.groundWaterSummer?.replaceAll(',','').replaceAll('%','')),
Number(item.groundWaterWinter?.replaceAll(',','').replaceAll('%',''))
];
// index가 0일 때만 rdi와 rdiRain 추가
console.log(item.rdiRain);
return index === 0 ? [...baseData, item.rdi&&item.rdi.toString().trim()!=''?Number(item.rdi.toString().replaceAll(',','')):'', item.rdiRain&&item.rdiRain.toString().trim()!=''?Number(item.rdiRain.toString().replaceAll(',','')):''] : baseData;
});
const sheetParameter = [
{
data: excelData1,
headerNum: 3,
headerRange: 'A1:G2',
sheetName: '운영실태',
startCol: 'A'
},
{
data: excelData2,
headerNum: 4,
headerRange: 'A1:Q3',
sheetName: '운영실태',
startCol: 'C'
const makeExcelData = async () => {
fetch(`/mainDashBoard.xlsx`, {}).then(async (response) => {
if (!response.ok) {
throw Error(response.statusText);
}
];
// 버퍼에 있는 엑셀데이터 전달 후 json으로 변환
await filePickedBlobArray(blobRet, '시설정보및운영현황황', sheetParameter);
});
// 받은 파일 바인너리 처리
const blobRet = await response.arrayBuffer();
const excelData1 = planFcltyList.map((item) => {
return [
item.item,
item.provisionalApproval?Number(item.provisionalApproval.toString().replaceAll(',',''))?Number(item.provisionalApproval.toString().replaceAll(',','')):item.provisionalApproval:'',
item.now,
item.stepOne,
item.stepTwo,
item.stepThree,
item.stepFour,
];
});
// operationalStatusList를 2차원 배열로 변환
const excelData2 = operationalStatusList.map((item, index) => {
console.log(item.yearAvg+' '+item.yearMax+' '+item.yearMin);
const baseData = [
Number(item.yearAvg?.toString().replaceAll(',','')),
Number(item.yearMax?.toString().replaceAll(',','')),
Number(item.yearMin?.toString().replaceAll(',','')),
Number(item.summerAll?.toString().replaceAll(',','')),
Number(item.summerThree?.toString().replaceAll(',','')),
Number(item.summerRain?.toString().replaceAll(',','')),
Number(item.winterAll?.toString().replaceAll(',','')),
Number(item.winterSunnyAvg?.toString().replaceAll(',','')),
Number(item.winterSunnyMax?.toString().replaceAll(',','')),
Number(item.winterSunnyMin?.toString().replaceAll(',','')),
Number(item.groundWaterAvg?.toString().replaceAll(',','').replaceAll('%','')),
Number(item.groundWaterSummer?.toString().replaceAll(',','').replaceAll('%','')),
Number(item.groundWaterWinter?.toString().replaceAll(',','').replaceAll('%',''))
];
// index가 0일 때만 rdi와 rdiRain 추가
return index === 0 ? [...baseData, item.rdi&&item.rdi.toString().trim()!=''?Number(item.rdi.toString().replaceAll(',','')):'', item.rdiRain&&item.rdiRain.toString().trim()!=''?Number(item.rdiRain.toString().replaceAll(',','')):''] : baseData;
});
const sheetParameter = [
{
data: excelData1,
headerNum: 3,
headerRange: 'A1:G2',
sheetName: '운영실태',
startCol: 'A'
},
{
data: excelData2,
headerNum: 4,
headerRange: 'A1:Q3',
sheetName: '운영실태',
startCol: 'C'
}
];
// 버퍼에 있는 엑셀데이터 전달 후 json으로 변환
await filePickedBlobArray(blobRet, '시설정보및운영현황황', sheetParameter);
});
};
break;
case 'mainGraph':
+7 -7
View File
@@ -83,10 +83,8 @@ export default function Rdi() {
const [operationValue, setOperationValue] = useState<string>("0");
// 조회기간
const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0");
useEffect(() => {
console.log(isLoading);
}, [isLoading]);
// 엑셀 다운로드 트리거
const [isInitExcelDownload, setIsInitExcelDownload] = useState<boolean>(false);
/**
* 페이지 함수
@@ -122,6 +120,7 @@ export default function Rdi() {
}, [isInit, initializeCommonData, searchRdiList, selectSearchYear]);
useEffect(() => {
if(!isInitExcelDownload) return ;
if (excelList && excelList.length > 0) {
downloadExcel({
data: excelList as unknown as JSON[],
@@ -130,7 +129,7 @@ export default function Rdi() {
headerRange: 'A1:O1'
});
}
}, [excelList]);
}, [excelList, isInitExcelDownload]);
/**
* 콜백함수
@@ -168,8 +167,9 @@ export default function Rdi() {
// 엑셀다운로드
const excelDownload = useCallback(() => {
searchRdi(ref.current, 1, 10000);
}, [searchRdi]);
searchRdi(ref.current, 1, rdiCount);
setIsInitExcelDownload(true);
}, [searchRdi, rdiCount]);
// 페이지 변경
const handlePageChange = useCallback((page: number) => {
@@ -21,7 +21,6 @@ export default function PlanSewage() {
,SewageOccurSearch
,initializeCommonData
,searchSewageOccurList
,resetExcelList
} = useFoStore((state) => state) || {
Common: { isInit: false, selectUpdownData: [], selectOperationData: [], selectSidoData: [], selectSigunData: [], selectRegionData: [], selectUnitWaterShedData: [], selectMiddleRegionData: [], selectEnvPartData: [], selectSearchYear: [{ val: "0" }], btnClass: "" },
SewageOccur: { sewageOccurList: [], sewageOccurCount: 0, excelList: [], upHeadList1: [], upHeadList2: [], upHeadList3: [] },
@@ -95,6 +94,8 @@ export default function PlanSewage() {
const [operationValue, setOperationValue] = useState<string>("0");
// 조회기간
const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0");
// 엑셀다운로드 트리거
const [isInitExcelDownload, setIsInitExcelDownload] = useState<boolean>(false);
/**
* 페이지 함수
@@ -136,14 +137,15 @@ export default function PlanSewage() {
* useEffect
*/
useEffect(() => {
resetExcelList();
if (!isInit) {
initializeCommonData();
searchSewageOccurList(1, 10, selectSearchYear[0]?.val || "0");
}
}, [isInit, initializeCommonData, searchSewageOccurList]);
}, [isInit, initializeCommonData, searchSewageOccurList, selectSearchYear]);
useEffect(() => {
if(!isInitExcelDownload) return ;
const timer = setTimeout(() => {
if (excelList && excelList.length > 0) {
downloadExcel({
@@ -156,7 +158,7 @@ export default function PlanSewage() {
}, 500); // 약간의 지연을 두고 다운로드 실행
return () => clearTimeout(timer);
}, [excelList, isInit]);
}, [excelList, isInit, isInitExcelDownload]);
/**
* 콜백함수
@@ -195,10 +197,7 @@ export default function PlanSewage() {
// 엑셀다운로드
const excelDownload = useCallback(() => {
searchFacilityPrecipitation(ref.current, 1, sewageOccurCount);
setTimeout(() => {
resetExcelList()
}, 1000); // 약간의 지연을 두고 초기화 (다운로드 완료 후)
setIsInitExcelDownload(true);
}, [searchFacilityPrecipitation, sewageOccurCount]);
// 페이지 변경
@@ -22,7 +22,6 @@ export default function UnderWaterInput() {
,UnderWaterSearch
,initializeCommonData
,searchUnderWaterList
,resetExcelList
} = useFoStore((state) => state) || {
Common: { isInit: false, selectUpdownData: [], selectOperationData: [], selectSidoData: [], selectSigunData: [], selectRegionData: [], selectUnitWaterShedData: [], selectMiddleRegionData: [], selectEnvPartData: [], selectSearchYear: [{ val: "0" }], btnClass: "" },
UnderWater: { underWaterList: [], underWaterCount: 0, excelList: [], upHeadList1: [], upHeadList2: [], upHeadList3: [] },
@@ -94,6 +93,8 @@ export default function UnderWaterInput() {
const [operationValue, setOperationValue] = useState<string>("0");
// 조회기간
const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0");
// 엑셀다운로드 트리거
const [isMount, setIsMount] = useState<boolean>(false);
/**
* 페이지 함수
@@ -135,14 +136,15 @@ export default function UnderWaterInput() {
* useEffect
*/
useEffect(() => {
resetExcelList();
if (!isInit) {
initializeCommonData();
searchUnderWaterList(1, 10, selectSearchYear[0]?.val || "0");
}
}, [isInit, initializeCommonData, searchUnderWaterList, selectSearchYear, resetExcelList]);
}, [isInit, initializeCommonData, searchUnderWaterList, selectSearchYear]);
useEffect(() => {
if(!isMount) return ;
const timer = setTimeout(() => {
if (excelList && excelList.length > 0) {
downloadExcel({
@@ -152,9 +154,9 @@ export default function UnderWaterInput() {
headerRange: 'A1:V1'
});
}
}, 500); // 약간의 지연을 두고 다운로드 실행
}, 1000); // 약간의 지연을 두고 다운로드 실행
return () => clearTimeout(timer);
}, [excelList]);
}, [excelList, isMount]);
/**
* 콜백함수
@@ -193,11 +195,8 @@ export default function UnderWaterInput() {
// 엑셀다운로드
const excelDownload = useCallback(() => {
searchFacilityPrecipitation(ref.current, 1, underWaterCount);
setTimeout(() => {
resetExcelList();
}, 1000); // 약간의 지연을 두고 다운로드 실행
}, [resetExcelList, searchFacilityPrecipitation, underWaterCount]);
setIsMount(true);
}, [searchFacilityPrecipitation, underWaterCount]);
// 페이지 변경
const handlePageChange = useCallback((page: number) => {
+68 -15
View File
@@ -21,16 +21,16 @@ export type RdiProps = {
lclgvrnmntNm : string,
daddr : string,
fctCpc : number,
summerMinBodValue : number,
summerMinBodValue : number | '',
inflAmnt : number | string | '',
winterAvgBodLowRainfall : number,
rdiiValue : number,
infoRnDay : number,
winterAvgBodLowRainfall : number | '',
rdiiValue : number | '',
infoRnDay : number | '',
rgnNm : string,
rvbsn : string,
lgbsn : string,
mdmbsn : string,
rainEffectDay : number, // 강우영향일
rainEffectDay : number | '', // 강우영향일
chungchunFlow : string | number | '',
rainCalInflAmnt : string | number | '',
properYn : string,
@@ -174,6 +174,18 @@ const fetchRdiList = async (pageNo: number, pageSize: number, searchYear: string
const tailStr = partCd ? '/'+ partCd + '/' + nameCd + '/' + fcltCpct + '/' + operationValue + (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') : '';
const url = '/rdii/list/' + pageNo + '/' + pageSize + '/' + searchYear + tailStr;
const res = await getFetch('fo', url);
res.data.forEach((item: RdiProps) => {
item.fctCpc = item.fctCpc && item.fctCpc > 0 ? Number(item.fctCpc.toString().trim()) : 0;
item.summerMinBodValue = '';
item.inflAmnt = '';
item.winterAvgBodLowRainfall = '';
item.rdiiValue = '';
item.infoRnDay = '';
item.properYn = '';
item.sido = item.lclgvrnmntNm.split(' ')[0]?.toString() || '';
item.sigungo = item.lclgvrnmntNm.split(' ')[1]?.toString() || '';
});
return { data: res.data, count: res.count }
}catch(error){
console.log(error);
@@ -185,6 +197,7 @@ const fetchRdiList = async (pageNo: number, pageSize: number, searchYear: string
* Store - expaort
====================================*/
export type RdiActions = {
resetExcelList: () => void;
searchRdiList: (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => void;
}
@@ -231,10 +244,32 @@ const dataFormSetting=(item : RdiProps)=> {
// Add type definitions for state setter function
type SetStateFunction = (state: RdiType | ((state: RdiType) => RdiType)) => void;
// 병렬 처리 제한용 헬퍼
async function processInBatches(items, batchSize, callback) {
const results:any = [];
for (let i = 0; i < items.length; i += batchSize) {
const batch = items.slice(i, i + batchSize);
const batchResults = await Promise.all(batch.map(callback));
results.push(...batchResults);
}
return results;
}
export type RdiStore = RdiType & RdiActions;
export const rdiReducer:(set:SetStateFunction)=>RdiActions=(set: SetStateFunction) => {
return {
resetExcelList: () => {
console.log('reset!!!!!');
set((state: RdiType) => ({
...state,
Rdi: {
...state.Rdi,
excelList: [],
},
}));
},
searchRdiList: async (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => {
set((state: RdiType) => ({
...state,
@@ -245,8 +280,31 @@ export const rdiReducer:(set:SetStateFunction)=>RdiActions=(set: SetStateFunctio
}));
const res = await fetchRdiList(pageNo, pageSize, searchYear, partCd, nameCd, fcltCpct, operationValue, fcltyNm);
if(pageSize>=res.count){
set((state: RdiType) => ({
...state,
Rdi: {
...state.Rdi,
excelList: [],
isLoading: true,
},
}));
}
else{
set((state: RdiType) => ({
...state,
Rdi: {
...state.Rdi,
rdiList: res.data,
excelList: [],
rdiCount: res.count,
isLoading: true,
},
}));
}
const data = res.data;
const reData = await Promise.all(data.map(async (item: RdiProps) => {
const reData = await processInBatches(data, 15, async (item: any, index: number) => {
const bscFctCd = item.bscFctCd;
const res2 = await getFetch('fo', '/fclt/flow/' + bscFctCd + '/' + searchYear) as FlowResponse;
const calData = CalcRainEffect(res2.data, 'SUNNY');
@@ -266,8 +324,8 @@ export const rdiReducer:(set:SetStateFunction)=>RdiActions=(set: SetStateFunctio
avgChungcunSumFlow = chungcunSumFlow&&chungcunSumFlow>0?chungcunSumFlow / calData.length : 0;
item.chungchunFlow = avgChungcunSumFlow;
item.rainCalInflAmnt = Number(avgChungcunSumFlow) + Number(item.rdiiValue);
return item;
}));
return {...item, index:index}
});
const excelDownList: ExcelType[] = [];
const rtnData = reData.map((item: RdiProps) => {
@@ -339,11 +397,6 @@ export const rdiReducer:(set:SetStateFunction)=>RdiActions=(set: SetStateFunctio
const roundMinSummerBodInfl = Number(roundNumber(minSummerBodInfl, 0).toString().replaceAll(',',''));
const roundMinSummerBod = Number(roundNumber(minSummerBod, 1).toString().replaceAll(',',''));
const roundWinterBodSunny = Number(roundNumber(winterBodSunny, 1).toString().replaceAll(',',''));
console.log('bsccd : ', item.bscFctCd);
console.log('roundMinSummerBodInfl : ',roundMinSummerBodInfl);
console.log('roundMinSummerBod : ',roundMinSummerBod);
console.log('roundWinterBodSunny : ',roundWinterBodSunny);
const winterBodAvg = Number(roundNumber((roundWinterBodSunny / winterCnt), 1).toString().replaceAll(',',''));
// 하절기(6~8월) 중 최저 유입 BOD일때 유입유량 x ((동절기 청천시 유입BOD - 하절기(6~8월) 중 최저 유입BOD) / (동절기 청천시 유입BOD))
@@ -374,7 +427,7 @@ export const rdiReducer:(set:SetStateFunction)=>RdiActions=(set: SetStateFunctio
dataFormSetting(item);
})
if(pageSize>5000){
if(pageSize>=res.count){
rtnData.forEach((item: RdiProps) => {
excelDownList.push({
시설코드: item.bscFctCd,
@@ -400,7 +453,7 @@ export const rdiReducer:(set:SetStateFunction)=>RdiActions=(set: SetStateFunctio
}
set((state: RdiType) => ({
...state,
Rdi: pageSize<5000?{
Rdi: pageSize<res.count?{
...state.Rdi,
rdiList: rtnData,
rdiCount: res.count,
+4 -12
View File
@@ -148,7 +148,6 @@ const fetchSweageOccurListFlow = async(fcltyCd : string, searchYear : string) =>
* Store - expaort
====================================*/
export type SewageOccurActions = {
resetExcelList: () => void;
searchSewageOccurList: (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => void;
searchSewageOccurDetailList: (bscFctCd: string, searchYear: string, startYearMonth?: string, endYearMonth?: string) => void;
}
@@ -230,16 +229,6 @@ export type FlowResponse = {
export const sewageOccurReducer:(set:any)=>SewageOccurActions=(set: any) => {
return {
resetExcelList: () => {
set((state: SewageOccurType) => ({
...state,
SewageOccur: {
...state.SewageOccur,
excelList: [],
},
}));
},
searchSewageOccurList: async (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => {
const facilityInitData = await fetchFacilityListInit(pageNo, pageSize, part, nameCd, fcltCpct, operationValue, fcltyNm);
if(pageSize < facilityInitData.count){
@@ -269,7 +258,10 @@ export const sewageOccurReducer:(set:any)=>SewageOccurActions=(set: any) => {
const calData = await processInBatches(fcltylist, 20, async (item: SewageOccurProps, index: number) => {
const bscFctCd = item.bscFctCd;
const res = await fetchSweageOccurListFlow(bscFctCd, searchYear);
const res = await fetchSweageOccurListFlow(bscFctCd, searchYear).catch(() => {
console.log('Error fetching flow data for facility code:', bscFctCd);
return { data: [], count: 0 };
});
item.sidoNm = item.sidoNm || '';
item.sggNm = item.sggNm || '';
+4 -12
View File
@@ -213,7 +213,6 @@ const fetchUnderWaterListFlow = async(fcltyCd : string, searchYear : string) =>
* Store - expaort
====================================*/
export type UnderWaterActions = {
resetExcelList: () => void;
searchUnderWaterList: (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => void;
unserWaterModal:(bscFctCd:string, searchYear:string, startYm:string, endYm:string)=>void
}
@@ -289,16 +288,6 @@ export type UnderWaterStore = UnderWaterType & UnderWaterActions;
export const underWaterReducer:(set:(fn: (state: UnderWaterType) => UnderWaterType) => void)=>UnderWaterActions=(set: (fn: (state: UnderWaterType) => UnderWaterType) => void) => {
return {
resetExcelList: () => {
set((state: UnderWaterType) => ({
...state,
UnderWater: {
...state.UnderWater,
excelList : [],
},
}));
},
searchUnderWaterList: async (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => {
const facilityInitData = await fetchFacilityListInit(pageNo, pageSize, part, nameCd, fcltCpct, operationValue, fcltyNm);
@@ -328,7 +317,10 @@ export const underWaterReducer:(set:(fn: (state: UnderWaterType) => UnderWaterTy
const calData = await processInBatches(fcltylist, 20, async (item: any, index: number) => {
const bscFctCd = item.bscFctCd;
const res = await fetchUnderWaterListFlow(bscFctCd, searchYear);
const res = await fetchUnderWaterListFlow(bscFctCd, searchYear).catch((error) => {
console.log('Error fetching flow data for facility code:', bscFctCd, error);
return { data: [], count: 0 }; // 에러 발생 시 빈 데이터 반환
});
item.sidoNm = item.sido || '';
item.sggNm = item.gugun || '';