From 800f3e4943fe8b4b396e7aee23964eb0da1a43f9 Mon Sep 17 00:00:00 2001 From: leedj312 Date: Fri, 24 Oct 2025 15:21:38 +0900 Subject: [PATCH 1/8] =?UTF-8?q?=EB=AC=BC=EC=9E=AC=EC=9D=B4=EC=9A=A9=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D=20=EB=B6=80=EB=B6=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/type.tsx | 6 +++--- apps/wr/components/ui/form/fcltReg/rainReg/rainRegForm.tsx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/type.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/type.tsx index 4bf891d87..57c5538f7 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/type.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/type.tsx @@ -30,9 +30,9 @@ export const baseReuseBuildingSchema = z.object({ crgRdctAmt: z.number().default(0), // 안쓰는 값 ruseFcltFlg: z.string().default(""), bdstUsgCd: z - .number() - .default(0) - .refine((val) => val === -1 || val >= 1, { + .string() + .default('000') + .refine((val) => val.trim().length > 0, { message: validationMessages.requiredSelect("건축물용도"), }), lawTrgtYn: z.string().default("Y"), diff --git a/apps/wr/components/ui/form/fcltReg/rainReg/rainRegForm.tsx b/apps/wr/components/ui/form/fcltReg/rainReg/rainRegForm.tsx index fbcae44b0..72affedc9 100644 --- a/apps/wr/components/ui/form/fcltReg/rainReg/rainRegForm.tsx +++ b/apps/wr/components/ui/form/fcltReg/rainReg/rainRegForm.tsx @@ -82,7 +82,7 @@ export default function RainRegForm({ operMngCd: item?.operMngCd ?? "", // null → "" grntchBdstCertYr: typeof item?.grntchBdstCertYr === "string" - ? item?.grntchBdstCertYr + ? item?.grntchBdstCertYr.trim() : "", // null → "" bdstUsgCd: item?.bdstUsgCd ?? Number("-1"), // null → "-1" instlCstSprtYn: item?.instlCstSprtYn ?? "Y", // null → "Y" From 05f25d27f47a179ea86942ec43a1df2e88049bbf Mon Sep 17 00:00:00 2001 From: ljpark Date: Fri, 24 Oct 2025 16:33:16 +0900 Subject: [PATCH 2/8] =?UTF-8?q?local=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=A3=BC=EC=84=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/.env.local | 1 + apps/wr/app/(with-layout)/stats/hasuStat/page.tsx | 0 2 files changed, 1 insertion(+) create mode 100644 apps/wr/app/(with-layout)/stats/hasuStat/page.tsx diff --git a/apps/wr/.env.local b/apps/wr/.env.local index 18a2464c8..fbce226c3 100644 --- a/apps/wr/.env.local +++ b/apps/wr/.env.local @@ -12,6 +12,7 @@ NEXT_PUBLIC_CS_TUS_UPLOAD = /attach-service/api/v1/tus/upload NEXT_PUBLIC_GEOSERVER_URL = https://hasudo.geo.rsonesoft.com/geoserver NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver NEXT_PUBLIC_WR_SERVER_API_URL = http://con.mind-one.co.kr:11083/reuse-service/api/v1 +#NEXT_PUBLIC_WR_SERVER_API_URL = http://192.168.1.222:8883/reuse-service/api/v1 NEXT_PUBLIC_SEWERAGE_API_URL = http://con.mind-one.co.kr:11083/sewerage-service # 개발 서버 API NEXT_PUBLIC_NGII_URL = https://map.ngii.go.kr NEXT_PUBLIC_NGII_PARAM = 04trYP9_xwLAfALjwZ-B8g&service=WMTS&request=GetTile&version=1.0.0&format=image/png&layer=korean_map&style=korean&tilematrixset=korean&tilematrix={z}&tilecol={x}&tilerow={y} diff --git a/apps/wr/app/(with-layout)/stats/hasuStat/page.tsx b/apps/wr/app/(with-layout)/stats/hasuStat/page.tsx new file mode 100644 index 000000000..e69de29bb From 1e14b464a468540421e49ae91c4532e30127261a Mon Sep 17 00:00:00 2001 From: ljpark Date: Fri, 24 Oct 2025 19:40:39 +0900 Subject: [PATCH 3/8] =?UTF-8?q?=ED=95=98=EC=88=98=EC=9E=AC=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9=ED=86=B5=EA=B3=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/(with-layout)/stats/hasuStat/page.tsx | 0 apps/wr/app/(without-layout)/layout.tsx | 13 ++++ .../(without-layout)/stats/hasuStat/page.tsx | 65 +++++++++++++++++++ apps/wr/app/api/client-ip/route.ts | 13 ++++ 4 files changed, 91 insertions(+) delete mode 100644 apps/wr/app/(with-layout)/stats/hasuStat/page.tsx create mode 100644 apps/wr/app/(without-layout)/layout.tsx create mode 100644 apps/wr/app/(without-layout)/stats/hasuStat/page.tsx create mode 100644 apps/wr/app/api/client-ip/route.ts diff --git a/apps/wr/app/(with-layout)/stats/hasuStat/page.tsx b/apps/wr/app/(with-layout)/stats/hasuStat/page.tsx deleted file mode 100644 index e69de29bb..000000000 diff --git a/apps/wr/app/(without-layout)/layout.tsx b/apps/wr/app/(without-layout)/layout.tsx new file mode 100644 index 000000000..3577697f5 --- /dev/null +++ b/apps/wr/app/(without-layout)/layout.tsx @@ -0,0 +1,13 @@ +"use client"; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + + + return ( + {children} + ); +} diff --git a/apps/wr/app/(without-layout)/stats/hasuStat/page.tsx b/apps/wr/app/(without-layout)/stats/hasuStat/page.tsx new file mode 100644 index 000000000..a0166aa30 --- /dev/null +++ b/apps/wr/app/(without-layout)/stats/hasuStat/page.tsx @@ -0,0 +1,65 @@ +"use client"; +import { useEffect, useState } from "react"; +import Link from "next/link"; + +export default function HasuStatPage() { + const [status, setStatus] = useState("checking"); + + useEffect(() => { + let didCancel = false; + + async function checkAndRedirect() { + try { + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), 3000); + + const res = await fetch(`/api/client-ip`, { signal: controller.signal }); + clearTimeout(timeout); + + if (!res.ok) throw new Error("no-ip"); + const data = await res.json(); + const ip: string = data?.ip || ""; + console.log("Client IP:", ip); + if (didCancel) return; + + const is10 = /^10\./.test(ip); + setStatus(is10 ? "redirect-internal" : "redirect-root"); + + // // Use location.replace so user can't go back to this page + // if (is10) { + // // redirect to internal IP + // // window.location.replace("http://10.101.163.102:8888/cms/lay1/WS10000014/program.do"); + // window.open('http://10.101.163.102:8888/cms/lay1/WS10000014/program.do', '_blank'); + // } else { + // //window.location.replace("https://www.hasudoinfo.or.kr/cms/lay1/WS10000014/program.do"); + // window.open('https://www.hasudoinfo.or.kr/cms/lay1/WS10000014/program.do', '_blank'); + // } + } catch { + // If anything fails, fall back to root + if (!didCancel) { + setStatus("error-fallback"); + window.location.replace("/"); + } + } + } + + checkAndRedirect(); + + return () => { + didCancel = true; + }; + }, []); + + return ( +
+ {status === "checking" &&
접속 IP 확인 중...
} + {status === "redirect-internal" &&
내부망으로 이동합니다...
} + {status === "redirect-root" &&
외부에서 접속하여 루트로 이동합니다...
} + {status === "error-fallback" && ( +
+ 문제가 발생하여 루트로 이동합니다. 이동하지 않으면 여기를 클릭하세요. +
+ )} +
+ ); +} diff --git a/apps/wr/app/api/client-ip/route.ts b/apps/wr/app/api/client-ip/route.ts new file mode 100644 index 000000000..f883ba9d1 --- /dev/null +++ b/apps/wr/app/api/client-ip/route.ts @@ -0,0 +1,13 @@ +import { NextResponse } from 'next/server'; + +export async function GET(request: Request) { + const headers = request.headers; + // Prefer x-forwarded-for (may be comma separated list) + const xff = headers.get('x-forwarded-for') ?? headers.get('x-real-ip') ?? ''; + let ip = ''; + if (xff && xff.length > 0) { + ip = String(xff).split(',')[0].trim(); + } + + return NextResponse.json({ ip }); +} From e71c50e9ad64c9b215c10265a799280b264e7903 Mon Sep 17 00:00:00 2001 From: dongsilove Date: Fri, 24 Oct 2025 23:42:03 +0900 Subject: [PATCH 4/8] =?UTF-8?q?fix:=20wr=20=ED=95=98=EC=88=98=EC=9E=AC?= =?UTF-8?q?=EC=9D=B4=EC=9A=A9=ED=86=B5=EA=B3=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/(without-layout)/layout.tsx | 2 +- .../(without-layout)/stats/hasuStat/page.tsx | 19 +++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/apps/wr/app/(without-layout)/layout.tsx b/apps/wr/app/(without-layout)/layout.tsx index 3577697f5..cbb9738cf 100644 --- a/apps/wr/app/(without-layout)/layout.tsx +++ b/apps/wr/app/(without-layout)/layout.tsx @@ -8,6 +8,6 @@ export default function RootLayout({ return ( - {children} + <>{children} ); } diff --git a/apps/wr/app/(without-layout)/stats/hasuStat/page.tsx b/apps/wr/app/(without-layout)/stats/hasuStat/page.tsx index a0166aa30..ee568e75a 100644 --- a/apps/wr/app/(without-layout)/stats/hasuStat/page.tsx +++ b/apps/wr/app/(without-layout)/stats/hasuStat/page.tsx @@ -19,21 +19,20 @@ export default function HasuStatPage() { if (!res.ok) throw new Error("no-ip"); const data = await res.json(); const ip: string = data?.ip || ""; - console.log("Client IP:", ip); if (didCancel) return; + alert("Client IP:" + ip); const is10 = /^10\./.test(ip); setStatus(is10 ? "redirect-internal" : "redirect-root"); - // // Use location.replace so user can't go back to this page - // if (is10) { - // // redirect to internal IP - // // window.location.replace("http://10.101.163.102:8888/cms/lay1/WS10000014/program.do"); - // window.open('http://10.101.163.102:8888/cms/lay1/WS10000014/program.do', '_blank'); - // } else { - // //window.location.replace("https://www.hasudoinfo.or.kr/cms/lay1/WS10000014/program.do"); - // window.open('https://www.hasudoinfo.or.kr/cms/lay1/WS10000014/program.do', '_blank'); - // } + // Use location.replace so user can't go back to this page + if (is10) { + // redirect to internal IP + window.open('http://10.101.163.102:8888/cms/lay1/WS10000014/program.do', '_blank'); + } else { + window.open('https://www.hasudoinfo.or.kr/cms/lay1/WS10000014/program.do', '_blank'); + } + window.history.back(); } catch { // If anything fails, fall back to root if (!didCancel) { From 4272a5c94438e92f2a608b03574862cc2f74b5e4 Mon Sep 17 00:00:00 2001 From: dongsilove Date: Sat, 25 Oct 2025 15:03:00 +0900 Subject: [PATCH 5/8] =?UTF-8?q?wr=20=ED=95=98=EC=88=98=EC=9E=AC=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9=ED=86=B5=EA=B3=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/dm/.env.production | 2 +- .../(without-layout)/stats/hasuStat/page.tsx | 5 ++-- apps/wr/app/api/client-ip/route.ts | 25 +++++++++++++++---- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/apps/dm/.env.production b/apps/dm/.env.production index 6f3cabc41..23cde9181 100644 --- a/apps/dm/.env.production +++ b/apps/dm/.env.production @@ -13,7 +13,7 @@ NEXT_PUBLIC_CS_SERVER_API_URL = http://apigateway.iipbs-was.svc.cluster.local:80 NEXT_PUBLIC_CS_TUS_UPLOAD = /attach-service/api/v1/tus/upload NEXT_PUBLIC_GEOSERVER_URL = http://172.29.143.87:8080/geoserver NEXT_PUBLIC_LX_GEOSERVER_URL = http://172.29.143.87:8080/geoserver_swl -NEXT_PUBLIC_REUSE_API_URL= http://apigateway.iipbs-was.svc.cluster.local:8000/reuse-service/api/v1 +NEXT_PUBLIC_WR_API_URL= http://apigateway.iipbs-was.svc.cluster.local:8000/reuse-service/api/v1 NEXT_PUBLIC_SEWERAGE_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000/sewerage-service NEXT_PUBLIC_NGII_URL = http://172.29.143.87 NEXT_PUBLIC_NGII_PARAM = BDE2BEF39DAFEC293AB6730150F222AC&service=WMTS&request=GetTile&version=1.0.0&format=image/png&layer=korean_map&style=korean&tilematrixset=korean&tilematrix={z}&tilecol={y}&tilerow={x} diff --git a/apps/wr/app/(without-layout)/stats/hasuStat/page.tsx b/apps/wr/app/(without-layout)/stats/hasuStat/page.tsx index ee568e75a..1d2bf2eb3 100644 --- a/apps/wr/app/(without-layout)/stats/hasuStat/page.tsx +++ b/apps/wr/app/(without-layout)/stats/hasuStat/page.tsx @@ -23,10 +23,11 @@ export default function HasuStatPage() { alert("Client IP:" + ip); const is10 = /^10\./.test(ip); - setStatus(is10 ? "redirect-internal" : "redirect-root"); + const is172 = /^172\./.test(ip); + setStatus(is10||is172 ? "redirect-internal" : "redirect-root"); // Use location.replace so user can't go back to this page - if (is10) { + if (is10||is172) { // redirect to internal IP window.open('http://10.101.163.102:8888/cms/lay1/WS10000014/program.do', '_blank'); } else { diff --git a/apps/wr/app/api/client-ip/route.ts b/apps/wr/app/api/client-ip/route.ts index f883ba9d1..016700fe7 100644 --- a/apps/wr/app/api/client-ip/route.ts +++ b/apps/wr/app/api/client-ip/route.ts @@ -2,11 +2,26 @@ import { NextResponse } from 'next/server'; export async function GET(request: Request) { const headers = request.headers; - // Prefer x-forwarded-for (may be comma separated list) - const xff = headers.get('x-forwarded-for') ?? headers.get('x-real-ip') ?? ''; - let ip = ''; - if (xff && xff.length > 0) { - ip = String(xff).split(',')[0].trim(); + // 1. x-forwarded-for 또는 x-real-ip에서 IP 추출 + let ip = headers.get('x-forwarded-for') || headers.get('x-real-ip') || ''; + + // IP가 있으면 첫 번째 IP 주소만 추출 + if (ip) { + if (ip.includes(',')) { + ip = ip.split(',')[0]?.trim()||''; + } else { + ip = ip.trim(); + } + } + + // 3. IPv6 주소 정규화 + if (ip.startsWith('::ffff:')) { + ip = ip.replace('::ffff:', ''); + } + + // 4. localhost IPv6를 IPv4로 변환 + if (ip === '::1') { + ip = '127.0.0.1'; } return NextResponse.json({ ip }); From b5e03c2430cffa1439bf87218cf00bd1db961bdb Mon Sep 17 00:00:00 2001 From: jsyang Date: Sun, 26 Oct 2025 22:13:07 +0900 Subject: [PATCH 6/8] =?UTF-8?q?=EC=A7=80=ED=95=98=EC=88=98=EC=9C=A0?= =?UTF-8?q?=EC=9E=85=EB=9F=89=20=EC=B6=94=EC=A0=95,=20=EC=98=A4=EC=88=98?= =?UTF-8?q?=EB=B0=9C=EC=83=9D=EB=9F=89=20=EC=B6=94=EC=A0=95=20=EC=86=8D?= =?UTF-8?q?=EB=8F=84=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/components/common/CalcRainEffect.tsx | 4 +- .../pages/(with-layout)/sewageOccur/page.tsx | 40 ++-- .../(with-layout)/underWaterInput/page.tsx | 31 ++- .../app/store/facilityPrecipitation/index.tsx | 1 + apps/fo/app/store/processFacility/index.tsx | 22 +- apps/fo/app/store/sewageOccur/index.tsx | 225 ++++++++++++++---- apps/fo/app/store/underWaterInput/index.tsx | 144 +++++++---- 7 files changed, 344 insertions(+), 123 deletions(-) diff --git a/apps/fo/app/components/common/CalcRainEffect.tsx b/apps/fo/app/components/common/CalcRainEffect.tsx index c35140d83..9a99564e7 100644 --- a/apps/fo/app/components/common/CalcRainEffect.tsx +++ b/apps/fo/app/components/common/CalcRainEffect.tsx @@ -10,8 +10,8 @@ */ export default function CalcRainEffect(data : any, weather='ALL') { - console.log("%c Calculate RainEffect !" , "background:blue;color:white"); - console.log("%c Data Length :"+data.length+" Weather : "+weather , "background:yellow;color:blue"); + /* console.log("%c Calculate RainEffect !" , "background:blue;color:white"); + console.log("%c Data Length :"+data.length+" Weather : "+weather , "background:yellow;color:blue");*/ // 강우영향일 계산 (강우량이 3 초과이면 무조건 강우영향일) for(let idx = 0; idx < data.length; idx++) { const item = data[idx]; diff --git a/apps/fo/app/pages/(with-layout)/sewageOccur/page.tsx b/apps/fo/app/pages/(with-layout)/sewageOccur/page.tsx index 6fb01efb3..87bc8e7a8 100644 --- a/apps/fo/app/pages/(with-layout)/sewageOccur/page.tsx +++ b/apps/fo/app/pages/(with-layout)/sewageOccur/page.tsx @@ -7,13 +7,13 @@ "use client" import { useFoStore } from "@/app/store"; -import type { TableUpperProps } from "@/app/store/common"; +import { type TableUpperProps } from "@/app/store/common"; +import type { SewageOccurProps } from "@/app/store/sewageOccur"; import { ContentsDiv, downloadExcel, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiModal, UiTable, type ReturnType } from "@common/business_components"; import ListDiv from "@common/business_components/ui/list_div"; +import LoadingOverlay from "@common/business_components/ui/loadingBar"; import { useCallback, useEffect, useRef, useState } from "react"; import SewageOccurModal from "./modal/page"; -import type { SewageOccurProps } from "@/app/store/sewageOccur"; -import LoadingOverlay from "@common/business_components/ui/loadingBar"; export default function PlanSewage() { const {Common @@ -21,6 +21,7 @@ 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: [] }, @@ -135,6 +136,7 @@ export default function PlanSewage() { * useEffect */ useEffect(() => { + resetExcelList(); if (!isInit) { initializeCommonData(); searchSewageOccurList(1, 10, selectSearchYear[0]?.val || "0"); @@ -142,15 +144,19 @@ export default function PlanSewage() { }, [isInit, initializeCommonData, searchSewageOccurList]); useEffect(() => { - if (excelList && excelList.length > 0) { - downloadExcel({ - data: excelList as unknown as JSON[], - fileName: '오수발생량 추정', - sheetNameArray: ['오수발생량 추정'], - headerRange: 'A1:O1' - }); - } - }, [excelList]); + const timer = setTimeout(() => { + if (excelList && excelList.length > 0) { + downloadExcel({ + data: excelList as unknown as JSON[], + fileName: '오수발생량 추정', + sheetNameArray: ['오수발생량 추정'], + headerRange: 'A1:O1' + }); + } + }, 500); // 약간의 지연을 두고 다운로드 실행 + + return () => clearTimeout(timer); + }, [excelList, isInit]); /** * 콜백함수 @@ -188,8 +194,12 @@ export default function PlanSewage() { // 엑셀다운로드 const excelDownload = useCallback(() => { - searchFacilityPrecipitation(ref.current, 1, 10000); - }, [searchFacilityPrecipitation]); + searchFacilityPrecipitation(ref.current, 1, sewageOccurCount); + + setTimeout(() => { + resetExcelList() + }, 1000); // 약간의 지연을 두고 초기화 (다운로드 완료 후) + }, [searchFacilityPrecipitation, sewageOccurCount]); // 페이지 변경 const handlePageChange = useCallback((page: number) => { @@ -250,4 +260,4 @@ export default function PlanSewage() { )} ); -} \ No newline at end of file +} diff --git a/apps/fo/app/pages/(with-layout)/underWaterInput/page.tsx b/apps/fo/app/pages/(with-layout)/underWaterInput/page.tsx index a621611b7..52184d591 100644 --- a/apps/fo/app/pages/(with-layout)/underWaterInput/page.tsx +++ b/apps/fo/app/pages/(with-layout)/underWaterInput/page.tsx @@ -22,6 +22,7 @@ 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: [] }, @@ -134,21 +135,25 @@ export default function UnderWaterInput() { * useEffect */ useEffect(() => { + resetExcelList(); if (!isInit) { initializeCommonData(); searchUnderWaterList(1, 10, selectSearchYear[0]?.val || "0"); } - }, [isInit, initializeCommonData, searchUnderWaterList, selectSearchYear]); + }, [isInit, initializeCommonData, searchUnderWaterList, selectSearchYear, resetExcelList]); useEffect(() => { - if (excelList && excelList.length > 0) { - downloadExcel({ - data: excelList as unknown as JSON[], - fileName: '지하수유입량 추정', - sheetNameArray: ['지하수유입량 추정'], - headerRange: 'A1:V1' - }); - } + const timer = setTimeout(() => { + if (excelList && excelList.length > 0) { + downloadExcel({ + data: excelList as unknown as JSON[], + fileName: '지하수유입량 추정', + sheetNameArray: ['지하수유입량 추정'], + headerRange: 'A1:V1' + }); + } + }, 500); // 약간의 지연을 두고 다운로드 실행 + return () => clearTimeout(timer); }, [excelList]); /** @@ -187,8 +192,12 @@ export default function UnderWaterInput() { // 엑셀다운로드 const excelDownload = useCallback(() => { - searchFacilityPrecipitation(ref.current, 1, 10000); - }, [searchFacilityPrecipitation]); + searchFacilityPrecipitation(ref.current, 1, underWaterCount); + + setTimeout(() => { + resetExcelList(); + }, 1000); // 약간의 지연을 두고 다운로드 실행 + }, [resetExcelList, searchFacilityPrecipitation, underWaterCount]); // 페이지 변경 const handlePageChange = useCallback((page: number) => { diff --git a/apps/fo/app/store/facilityPrecipitation/index.tsx b/apps/fo/app/store/facilityPrecipitation/index.tsx index 984a40814..f7a716349 100644 --- a/apps/fo/app/store/facilityPrecipitation/index.tsx +++ b/apps/fo/app/store/facilityPrecipitation/index.tsx @@ -252,6 +252,7 @@ export const facilityPrecipitationReducer:(set:(fn: (state: FacilityPrecipitatio const excelDownList: ExcelType[] = []; if(pageSize>5000){ + console.log('엑셀다운로드 데이터 생성 : ', data.length); data.forEach((item: FacilityPrecipitationProps) => { excelDownList.push({ 시설코드: item.bscFctCd, diff --git a/apps/fo/app/store/processFacility/index.tsx b/apps/fo/app/store/processFacility/index.tsx index 43e7eab53..5cdab43b6 100644 --- a/apps/fo/app/store/processFacility/index.tsx +++ b/apps/fo/app/store/processFacility/index.tsx @@ -1,5 +1,5 @@ import { getFetch } from "@/app/components/api"; -import DataForm from "@/app/components/common/DataForm"; +import DataForm, { roundNumber } from "@/app/components/common/DataForm"; import type { TableUpperProps } from "../dashboard"; export type { TableUpperProps }; @@ -412,6 +412,26 @@ const gridListFcltyWaterQltyInfo:fcltyWaterQltyInfoProps[] = [ ====================================*/ // 시설정보 리스트 데이터 호출(구분 선택 후 시설명 있을 경우) +export const fetchFacilityListInit = async (pageNo:number, pageSize:number, partValue?:string, nameCd?:string, fcltCpct?:string, oprtngSttsCd?:string, facilityPartValue?:string, facilityName?:string ): Promise<{data:facilityListProps[], count:number}> => { + try { + const tailStr = partValue ? '/'+ (partValue==='G' ? 'A' : partValue) + '/' + nameCd + '/' + fcltCpct + '/' + oprtngSttsCd + '/' + facilityPartValue + (facilityName && facilityName !== '' ? '/' + facilityName : '') : ''; + const url = '/prcsFclt/info/'+pageNo+'/'+pageSize+tailStr; + const res = await getFetch('fo', url); + res.data.forEach((item: Record) => { + item.daddr = item.addr ? item.addr : ''; + item.sidoNm = item.sido ? item.sido : ''; + item.sggNm = item.gugun ? item.gugun : ''; + item.fctCpc = roundNumber(item.fctCpc as number, 0); + }); + + return res; + } catch (error) { + console.error('시설정보 데이터를 가져오는데 실패했습니다:', error); + return {data: [], count: 0}; + } +}; + + const fetchFacilityList = async (pageNo:number, pageSize:number, partValue?:string, nameCd?:string, fcltCpct?:string, oprtngSttsCd?:string, facilityPartValue?:string, facilityName?:string ): Promise<{data:facilityListProps[], count:number}> => { try { const tailStr = partValue ? '/'+ (partValue==='G' ? 'A' : partValue) + '/' + nameCd + '/' + fcltCpct + '/' + oprtngSttsCd + '/' + facilityPartValue + (facilityName && facilityName !== '' ? '/' + facilityName : '') : ''; diff --git a/apps/fo/app/store/sewageOccur/index.tsx b/apps/fo/app/store/sewageOccur/index.tsx index e9ae8985b..5805f18f7 100644 --- a/apps/fo/app/store/sewageOccur/index.tsx +++ b/apps/fo/app/store/sewageOccur/index.tsx @@ -1,8 +1,9 @@ -import DataForm from "@/app/components/common/DataForm"; -import type { TableUpperProps } from "../dashboard"; import { getFetch } from "@/app/components/api"; import CalcRainEffect from "@/app/components/common/CalcRainEffect"; +import { roundNumber } from "@/app/components/common/DataForm"; import { OSU_PERCENT } from "../common"; +import type { TableUpperProps } from "../dashboard"; +import { fetchFacilityListInit } from "../processFacility"; export type { TableUpperProps }; /**==================================== * Store - 타입 @@ -124,24 +125,30 @@ const tempSewageOccurList:modalTableType[] = [] * Store - API ====================================*/ -const fetchSewageOccurList = async (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => { +/*const fetchSewageOccurList = async (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => { // 20250701임시시 const tailStr = partCd ? '/'+ partCd + '/' + nameCd + '/' + fcltCpct + '/' + operationValue + (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') : ''; const url = '/fclt/flow/' + pageNo + '/' + pageSize + '/' + searchYear + tailStr; const res = await getFetch('fo', url); return { data: res.data, count: res.count } - /* const tailStr = partCd ? '/'+ partCd + '/' + nameCd + '/' + fcltCpct + '/' + operationValue + (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') : ''; const url = '/fclt/qlty/' + pageNo + '/' + pageSize + '/' + searchYear + tailStr; const res = await getFetch('fo', url); - return { data: res.data, count: res.count } */ + return { data: res.data, count: res.count } +}*/ + +const fetchSweageOccurListFlow = async(fcltyCd : string, searchYear : string) => { + const url = '/fclt/flow/' + fcltyCd + '/' + searchYear; + const res = await getFetch('fo', url); + return res; } /**==================================== * 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; } @@ -194,6 +201,17 @@ export const sewageOccurInitState: SewageOccurType = { } } +// 병렬 처리 제한용 헬퍼 +async function processInBatches(items, batchSize, callback) { + const results = []; + 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 SewageOccurStore = SewageOccurType & SewageOccurActions; // Add type definitions for API responses @@ -212,33 +230,58 @@ export type FlowResponse = { export const sewageOccurReducer:(set:any)=>SewageOccurActions=(set: any) => { return { - searchSewageOccurList: async (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => { + resetExcelList: () => { set((state: SewageOccurType) => ({ - ...state, - SewageOccur: { - ...state.SewageOccur, - isLoading: true, - }, + ...state, + SewageOccur: { + ...state.SewageOccur, + excelList: [], + }, })); - const res = await fetchSewageOccurList(pageNo, pageSize, searchYear, part, nameCd, fcltCpct, operationValue, fcltyNm); - const excelDownList: ExcelType[] = []; - const data = res.data; - // Promise.all을 사용하여 모든 비동기 작업이 완료될 때까지 기다림 - const calData = await Promise.all(data.map(async (item: SewageOccurProps) => { - item.sidoNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || ''; - item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || ''; + }, + + 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){ + set((state: SewageOccurType) => ({ + ...state, + SewageOccur: { + ...state.SewageOccur, + sewageOccurList : facilityInitData.data, + sewageOccurCount : facilityInitData.count, + excelList : [], + isLoading: true + }, + })); + }else{ + set((state: SewageOccurType) => ({ + ...state, + SewageOccur: { + ...state.SewageOccur, + excelList : [], + isLoading: true + }, + })); + } + + const fcltylist = facilityInitData.data; + const fcltyCount = facilityInitData.count; + + const calData = await processInBatches(fcltylist, 20, async (item: SewageOccurProps, index: number) => { + const bscFctCd = item.bscFctCd; + const res = await fetchSweageOccurListFlow(bscFctCd, searchYear); + + item.sidoNm = item.sidoNm || ''; + item.sggNm = item.sggNm || ''; item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, '')); item.swageChangePercent = 90; - const bscFctCd = item.bscFctCd; - const res2 = await getFetch('fo', '/fclt/flow/' + bscFctCd + '/' + searchYear) as FlowResponse; - let sumChungcunDay = 0; let chungcunSumFlow = 0; let totWaterUsage = 0; // 강우영향일 계산 (강우량이 3 초과이면 무조건 강우영향일) - const rainEffectData = CalcRainEffect(res2.data); + const rainEffectData = CalcRainEffect(res.data); rainEffectData.forEach((item: FlowDataItem) => { @@ -246,27 +289,25 @@ export const sewageOccurReducer:(set:any)=>SewageOccurActions=(set: any) => { chungcunSumFlow += Number(item.inflAmnt); sumChungcunDay += 1; } - totWaterUsage += Number(item.inflAmnt); }); - const avg = totWaterUsage /res2.count; + + const avg = res.count == 0 ? 0 : totWaterUsage /res.count; item.waterUsage = parseFloat((avg * 0.65).toFixed(2)); item.sewageInputYear = parseFloat(avg.toFixed(2)); item.sewageInputChungchun = parseFloat((chungcunSumFlow===0?0:(chungcunSumFlow / sumChungcunDay)).toFixed(2)); item.estimationSewage = parseFloat((item.waterUsage * (item.swageChangePercent/100)).toFixed(2)); - return item; - })); - calData.forEach((item: SewageOccurProps) => { - DataForm({ type: 'float0', column: 'fctCpc', item: item as unknown as Record }); - DataForm({ type: 'float0', column: 'waterUsage', item: item as unknown as Record }); - DataForm({ type: 'float0', column: 'swageChangePercent', item: item as unknown as Record }); - DataForm({ type: 'float0', column: 'estimationSewage', item: item as unknown as Record }); - DataForm({ type: 'float0', column: 'sewageInputYear', item: item as unknown as Record }); - DataForm({ type: 'float0', column: 'sewageInputChungchun', item: item as unknown as Record }); + // 반올림 처리 + ["fctCpc", "waterUsage", "swageChangePercent", "estimationSewage", "sewageInputYear", "sewageInputChungchun"].forEach( + (key) => (item[key] = roundNumber(item[key], 0)) + ); + + return {...item, index:index}; }); - if(pageSize>5000){ + if(pageSize >= fcltyCount){ + const excelDownList: ExcelType[] = []; calData.forEach((item: SewageOccurProps) => { excelDownList.push({ 시설코드: item.bscFctCd, @@ -286,21 +327,109 @@ export const sewageOccurReducer:(set:any)=>SewageOccurActions=(set: any) => { "청천시하수유입량(m³/일)": item.sewageInputChungchun, }); }); + set((state: SewageOccurType) => ({ + ...state, + SewageOccur: { + ...state.SewageOccur, + excelList: excelDownList, + isLoading : false, + }, + })); + }else{ + set((state: SewageOccurType) => ({ + ...state, + SewageOccur: { + ...state.SewageOccur, + sewageOccurList : calData, + sewageOccurCount : facilityInitData.count, + isLoading: false, + excelList: [], + }, + })); } - // 조회 데이터 설정 - set((state: any) => ({ - ...state, - SewageOccur: pageSize<5000?{ - ...state.SewageOccur, - sewageOccurList: calData, - sewageOccurCount: res.count, - isLoading : false, - }:{ - ...state.SewageOccur, - excelList: excelDownList, - isLoading : false, - }, - })) + + /* fetchSewageOccurList(pageNo, pageSize, searchYear, part, nameCd, fcltCpct, operationValue, fcltyNm).then(async (res) => { + const excelDownList: ExcelType[] = []; + const data = res.data; + // Promise.all을 사용하여 모든 비동기 작업이 완료될 때까지 기다림 + const calData = await Promise.all(data.map(async (item: SewageOccurProps) => { + item.sidoNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || ''; + item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || ''; + item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, '')); + item.swageChangePercent = 90; + + const bscFctCd = item.bscFctCd; + const res2 = await getFetch('fo', '/fclt/flow/' + bscFctCd + '/' + searchYear) as FlowResponse; + + let sumChungcunDay = 0; + let chungcunSumFlow = 0; + let totWaterUsage = 0; + + // 강우영향일 계산 (강우량이 3 초과이면 무조건 강우영향일) + const rainEffectData = CalcRainEffect(res2.data); + + rainEffectData.forEach((item: FlowDataItem) => { + + if(Number(item.rainEffectDay) === 0){ + chungcunSumFlow += Number(item.inflAmnt); + sumChungcunDay += 1; + } + + totWaterUsage += Number(item.inflAmnt); + }); + const avg = totWaterUsage /res2.count; + item.waterUsage = parseFloat((avg * 0.65).toFixed(2)); + item.sewageInputYear = parseFloat(avg.toFixed(2)); + item.sewageInputChungchun = parseFloat((chungcunSumFlow===0?0:(chungcunSumFlow / sumChungcunDay)).toFixed(2)); + item.estimationSewage = parseFloat((item.waterUsage * (item.swageChangePercent/100)).toFixed(2)); + return item; + })); + + calData.forEach((item: SewageOccurProps) => { + DataForm({ type: 'float0', column: 'fctCpc', item: item as unknown as Record }); + DataForm({ type: 'float0', column: 'waterUsage', item: item as unknown as Record }); + DataForm({ type: 'float0', column: 'swageChangePercent', item: item as unknown as Record }); + DataForm({ type: 'float0', column: 'estimationSewage', item: item as unknown as Record }); + DataForm({ type: 'float0', column: 'sewageInputYear', item: item as unknown as Record }); + DataForm({ type: 'float0', column: 'sewageInputChungchun', item: item as unknown as Record }); + }); + + if(pageSize>5000){ + calData.forEach((item: SewageOccurProps) => { + excelDownList.push({ + 시설코드: item.bscFctCd, + 권역: item.rvbsn, + 단위유역: item.lgbsn, + 중권역: item.mdmbsn, + 환경청: item.rgnNm, + 시설명: item.bscFctNm, + 시도: item.sidoNm, + 시군구: item.sggNm, + 주소: item.daddr, + "시설용량(m³/일)": item.fctCpc, + "물사용량(m³/일)": item.waterUsage, + "오수전환율(%)": item.swageChangePercent, + "오수발생량추정(m³/일)": item.estimationSewage, + "연간하수유입량(m³/일)": item.sewageInputYear, + "청천시하수유입량(m³/일)": item.sewageInputChungchun, + }); + }); + } + // 조회 데이터 설정 + set((state: any) => ({ + ...state, + SewageOccur: pageSize<5000?{ + ...state.SewageOccur, + sewageOccurList: calData, + sewageOccurCount: res.count, + isLoading : false, + }:{ + ...state.SewageOccur, + excelList: excelDownList, + isLoading : false, + }, + })) + }); */ }, searchSewageOccurDetailList: async (bscFctCd: string, searchYear: string, startYearMonth?: string, endYearMonth?: string) => { diff --git a/apps/fo/app/store/underWaterInput/index.tsx b/apps/fo/app/store/underWaterInput/index.tsx index f6b466492..276098195 100644 --- a/apps/fo/app/store/underWaterInput/index.tsx +++ b/apps/fo/app/store/underWaterInput/index.tsx @@ -1,9 +1,10 @@ import { getFetch } from "@/app/components/api"; -import DataForm from "@/app/components/common/DataForm"; +import DataForm, { roundNumber } from "@/app/components/common/DataForm"; import type { TableUpperProps } from "../dashboard"; import type { ExcelType, UnderWaterInputModalProps, UnderWaterProps } from "./types"; import CalcRainEffect from "@/app/components/common/CalcRainEffect"; import { OSU_PERCENT } from "../common"; +import { fetchFacilityListInit } from "../processFacility"; export type { TableUpperProps }; // SewageOccurDetailProps 타입을 여기에 정의 @@ -187,24 +188,32 @@ export interface ApiResponse { count: number; } +/* const fetchUnderWaterList = async (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => { const tailStr = partCd ? '/'+ partCd + '/' + nameCd + '/' + fcltCpct + '/' + operationValue + (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') : ''; const url = '/fclt/flow/' + pageNo + '/' + pageSize + '/' + searchYear + tailStr; const res = await getFetch('fo', url); return { data: res.data, count: res.count } - /* + const tailStr = partCd ? '/'+ partCd + '/' + nameCd + '/' + fcltCpct + '/' + operationValue + (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') : ''; const url = '/fclt/qlty/' + pageNo + '/' + pageSize + '/' + searchYear + tailStr; const res = await getFetch('fo', url); - return { data: res.data, count: res.count } */ + return { data: res.data, count: res.count } //return { data: underWaterList, count: 2 }; +}*/ + +const fetchUnderWaterListFlow = async(fcltyCd : string, searchYear : string) => { + const url = '/fclt/flow/' + fcltyCd + '/' + searchYear; + const res = await getFetch('fo', url); + return res; } /**==================================== * 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 } @@ -220,7 +229,7 @@ export type UnderWaterType = { upHeadList1: TableUpperProps[]; upHeadList2: TableUpperProps[]; upHeadList3: TableUpperProps[]; - underWaterList: UnderWaterProps[]; // Changed from facilityAreaProps to ProcessAreaProps + underWaterList: any[]; // Changed from facilityAreaProps to ProcessAreaProps underWaterCount: number; excelList: ExcelType[]; isLoading: boolean; @@ -265,29 +274,65 @@ const dataFormSetting=(item : UnderWaterProps)=> { DataForm({ type: 'float0', column: 'underWaterInputWinter', item: item as unknown as Record}); } +// 병렬 처리 제한용 헬퍼 +async function processInBatches(items, batchSize, callback) { + const results = []; + 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 UnderWaterStore = UnderWaterType & UnderWaterActions; export const underWaterReducer:(set:(fn: (state: UnderWaterType) => UnderWaterType) => void)=>UnderWaterActions=(set: (fn: (state: UnderWaterType) => UnderWaterType) => void) => { return { - searchUnderWaterList: async (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => { + resetExcelList: () => { set((state: UnderWaterType) => ({ ...state, UnderWater: { ...state.UnderWater, - isLoading: true, + excelList : [], }, })); - - const res = await fetchUnderWaterList(pageNo, pageSize, searchYear, part, nameCd, fcltCpct, operationValue, fcltyNm); - const data = res.data; - const calData = await Promise.all(data.map(async (item: UnderWaterProps) => { - item.sidoNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || ''; - item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || ''; - item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, '')); + }, + 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); + + if(pageSize < facilityInitData.count){ + set((state: UnderWaterType) => ({ + ...state, + UnderWater: { + ...state.UnderWater, + underWaterList : facilityInitData.data, + underWaterCount : facilityInitData.count, + isInit : true, + isLoading: true, + }, + })); + }else{ + set((state: UnderWaterType) => ({ + ...state, + UnderWater: { + ...state.UnderWater, + isLoading: true, + }, + })); + } + + const fcltylist = facilityInitData.data; + const fcltyCount = facilityInitData.count; + + const calData = await processInBatches(fcltylist, 20, async (item: any, index: number) => { const bscFctCd = item.bscFctCd; - const res2 = await getFetch('fo', '/fclt/flow/' + bscFctCd + '/' + searchYear) as ApiResponse; - const calData = CalcRainEffect(res2.data, 'SUNNY'); + const res = await fetchUnderWaterListFlow(bscFctCd, searchYear); + + item.sidoNm = item.sido || ''; + item.sggNm = item.gugun || ''; + item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, '')); let chungcunSumFlow = 0; @@ -297,7 +342,8 @@ export const underWaterReducer:(set:(fn: (state: UnderWaterType) => UnderWaterTy let sumWinterChungcunDay = 0; let sumWinterChungcunFlow = 0; - calData.forEach((flowItem: FlowDataItem) => { + const rainEffectData = CalcRainEffect(res.data, 'SUNNY'); + rainEffectData.forEach((flowItem: FlowDataItem) => { chungcunSumFlow += flowItem.inflAmnt && flowItem.inflAmnt !== 'x' ? Number(flowItem.inflAmnt.toString().trim()) : 0; if(flowItem.msrDt.substring(4,6) ==='06' || flowItem.msrDt.substring(4,6) ==='07' || flowItem.msrDt.substring(4,6) ==='08'){ @@ -308,25 +354,32 @@ export const underWaterReducer:(set:(fn: (state: UnderWaterType) => UnderWaterTy sumWinterChungcunFlow += flowItem.inflAmnt && flowItem.inflAmnt !== 'x' ? Number(flowItem.inflAmnt.toString().trim()) : 0; } }); - const usage = chungcunSumFlow&&chungcunSumFlow>0?chungcunSumFlow / calData.length:0; + + let totWaterInflow = 0 + + const rainEffectTot = CalcRainEffect(res.data); + rainEffectTot.forEach((flowItem: FlowDataItem) => { + totWaterInflow += flowItem.inflAmnt && flowItem.inflAmnt !== 'x' ? Number(flowItem.inflAmnt.toString().trim()) : 0; + }); + + + const avg = res.count === 0 ? 0 : totWaterInflow / res.count; + + const usage = chungcunSumFlow&&chungcunSumFlow>0?chungcunSumFlow / rainEffectData.length:0; const summerUsage = sumSummerChungcunFlow?sumSummerChungcunFlow / sumSummerChungcunDay:0; const winterUsage = sumWinterChungcunFlow?sumWinterChungcunFlow / sumWinterChungcunDay:0; - //bscFctCd === '11200PB001R') 중랑 - 테스트할 때 코드넣고 해야 보기편해요~ - /* if(bscFctCd === '11200PB001R') { - console.log('calData.length : ', calData.length); - console.log('usage : ', usage, 'summerUsage : ', summerUsage, 'winterUsage : ', winterUsage); - } */ + item.inflAmnt = roundNumber(avg, 0); item.underWaterInputYear = Math.round((usage-(usage*0.65*OSU_PERCENT)) * 1) / 1; item.underWaterInputSummer = Math.round((summerUsage-(summerUsage*0.65*OSU_PERCENT)) * 1) / 1; item.underWaterInputWinter = Math.round((winterUsage-(winterUsage*0.65*OSU_PERCENT)) * 1) / 1; item.properYn = Math.round( (Number(usage-(usage*0.65*OSU_PERCENT)) / Number(item.inflAmnt)) * 100 * 1 ) / 1 > 20?'초과유입':'적정'; - return item; - })); + return {...item, index:index}; + }); - const excelDownList: ExcelType[] = []; - if(pageSize>5000){ - calData.forEach(item => { + if(pageSize>= fcltyCount){ + const excelDownList: ExcelType[] = []; + calData.forEach((item : any) => { dataFormSetting(item) item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, '')); /* item.sidoNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || ''; @@ -349,27 +402,26 @@ export const underWaterReducer:(set:(fn: (state: UnderWaterType) => UnderWaterTy 적정여부: item.properYn, }); }); + set((state: UnderWaterType) => ({ + ...state, + UnderWater: { + ...state.UnderWater, + excelList: excelDownList, + isLoading : false, + }, + })); }else{ - data.forEach((item: UnderWaterProps) => { - dataFormSetting(item) - item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, '')); - /* item.sidoNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || ''; - item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || ''; */ - }); + set((state: UnderWaterType) => ({ + ...state, + UnderWater: { + ...state.UnderWater, + underWaterList: calData, + underWaterCount: facilityInitData.count, + excelList: [], + isLoading : false, + }, + })); } - set((state: UnderWaterType) => ({ - ...state, - UnderWater: pageSize<5000?{ - ...state.UnderWater, - underWaterList: calData, - underWaterCount: res.count, - isLoading : false, - }:{ - ...state.UnderWater, - isLoading : false, - excelList: excelDownList - }, - })) }, unserWaterModal:async (bscFctCd, searchYear, startYm, endYm)=>{ From 07b65a90063aef13f7ee0062480da98d928e43ce Mon Sep 17 00:00:00 2001 From: jsyang Date: Sun, 26 Oct 2025 22:31:17 +0900 Subject: [PATCH 7/8] =?UTF-8?q?=EB=B9=8C=EB=93=9C=EC=97=90=EB=9F=AC?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/fo/app/store/sewageOccur/index.tsx | 2 +- apps/fo/app/store/underWaterInput/index.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/fo/app/store/sewageOccur/index.tsx b/apps/fo/app/store/sewageOccur/index.tsx index 5805f18f7..2f22cad81 100644 --- a/apps/fo/app/store/sewageOccur/index.tsx +++ b/apps/fo/app/store/sewageOccur/index.tsx @@ -203,7 +203,7 @@ export const sewageOccurInitState: SewageOccurType = { // 병렬 처리 제한용 헬퍼 async function processInBatches(items, batchSize, callback) { - const results = []; + 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)); diff --git a/apps/fo/app/store/underWaterInput/index.tsx b/apps/fo/app/store/underWaterInput/index.tsx index 276098195..a3238b306 100644 --- a/apps/fo/app/store/underWaterInput/index.tsx +++ b/apps/fo/app/store/underWaterInput/index.tsx @@ -276,7 +276,7 @@ const dataFormSetting=(item : UnderWaterProps)=> { // 병렬 처리 제한용 헬퍼 async function processInBatches(items, batchSize, callback) { - const results = []; + 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)); From 57a4fa54eb858dd8ffc89cac244a3db6ee61d723 Mon Sep 17 00:00:00 2001 From: ljpark Date: Mon, 27 Oct 2025 09:45:26 +0900 Subject: [PATCH 8/8] fo 443,443 --- apps/fo/middleware.ts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/apps/fo/middleware.ts b/apps/fo/middleware.ts index cca7ecd22..320f7ee2f 100644 --- a/apps/fo/middleware.ts +++ b/apps/fo/middleware.ts @@ -33,9 +33,20 @@ function buildAuthHeaders(req: NextRequest): Headers { /** 공통: rewrite 응답 생성 (헤더 주입 여부 선택) */ function rewriteWith(url: string, req: NextRequest, withAuth = false): NextResponse { - return withAuth - ? NextResponse.rewrite(new URL(url), { headers: buildAuthHeaders(req) }) - : NextResponse.rewrite(new URL(url)); + + const headers = withAuth ? buildAuthHeaders(req) : new Headers(req.headers); + + if (url.indexOf("/operation-service") !== -1) { + // x-forwarded- 로 시작하는 헤더의 첫 번째 값만 남김 + for (const [key, value] of headers.entries()) { + if (key.startsWith('x-forwarded-') && value.includes(',')) { + const first = (value?.split(',')[0] || '').trim(); + headers.set(key, first); + } + } + } + + return NextResponse.rewrite(new URL(url), { headers }); } export function middleware(req: NextRequest) {