This commit is contained in:
2025-12-17 18:41:53 +09:00
5 changed files with 159 additions and 159 deletions
+76 -50
View File
@@ -3,7 +3,7 @@ import { client } from "@/shared/api";
import { create } from "zustand";
import { ENDPOINT } from "@/shared/config";
import type { PplLengthContent, SewerageService } from "../model/sn-interface";
import type { OldPplChartData,mapPplOldType,mapPplOldItem } from "../model/sn-interface";
import type { OldPplChartData, mapPplOldType, mapPplOldItem } from "../model/sn-interface";
export type SnState = {
snStatus: PplLengthContent[] | null;
@@ -13,48 +13,48 @@ export type SnStateActions = {
};
export type SnStore = SnState & SnStateActions;
const initialUsePplChartData: OldPplChartData={
"labels": ["금강","한강","낙동강", "영산강"],
"datasets": [
{
"label": "기타",
"data": [1966,15480,6957,1936],
"backgroundColor": "rgba(255, 178, 150, 0.9)"
},
{
"label": "오수관로",
"data": [8074,15879,16337,5671],
"backgroundColor": "rgba(180, 240, 190, 0.95)"
},
{
"label": "우수관로",
"data": [4838,12893,10492,4281],
"backgroundColor": "rgba(170, 210, 245, 0.95)"
}
]
const initialUsePplChartData: OldPplChartData = {
labels: ["금강", "한강", "낙동강", "영산강"],
datasets: [
{
label: "기타",
data: [1966, 15480, 6957, 1937],
backgroundColor: "rgba(255, 178, 150, 0.9)"
},
{
label: "오수관로",
data: [8074, 15879, 16337, 5671],
backgroundColor: "rgba(180, 240, 190, 0.95)"
},
{
label: "우수관로",
data: [4838, 12893, 10492, 4281],
backgroundColor: "rgba(170, 210, 245, 0.95)"
}
]
};
const initialOldPplChartData: OldPplChartData={
labels: ["금강","한강","낙동강","영산강"],
datasets: [
{
label: "20-25년",
data: [ 1140,3181,3100,1662],
backgroundColor: "#3B82F6",
borderRadius: 4
},
{
label: "25-30년",
data: [1391, 3615, 1814, 1411],
backgroundColor: "#EF4444",
borderRadius: 4
},
{
label: "30년이상",
data: [ 2014, 10303,9831,2000],
backgroundColor: "#F87171",
borderRadius: 4
}
]
const initialOldPplChartData: OldPplChartData = {
labels: ["금강", "한강", "낙동강", "영산강"],
datasets: [
{
label: "20-25년",
data: [1140, 3181, 3100, 1662],
backgroundColor: "#3B82F6",
borderRadius: 4
},
{
label: "25-30년",
data: [1391, 3615, 1814, 1411],
backgroundColor: "#EF4444",
borderRadius: 4
},
{
label: "30년이상",
data: [2014, 10303, 9831, 2000],
backgroundColor: "#F87171",
borderRadius: 4
}
]
};
type OldPplStoreState = {
chartData: OldPplChartData;
@@ -70,13 +70,13 @@ export const useUsePplStore = create<OldPplStoreState>((set) => ({
}));
export const initailMapOldPplData: mapPplOldType = {
colTitle: {totalLen: "총길이",pplLen2025: "20~25년",pplLen2530: "25~30년",pplLen30 : "30년 이상" }, //헤더 영역
data: [
{name: "금강", totalLen: 4545, pplLen2025: 1140, pplLen2530: 1391, pplLen30 : 2014},
{name: "한강", totalLen: 17099, pplLen2025: 3181, pplLen2530: 3615, pplLen30 : 10303},
{name: "낙동강", totalLen: 14745, pplLen2025: 3100, pplLen2530: 1814, pplLen30 : 9831},
{name: "영산강", totalLen: 5073, pplLen2025: 1662, pplLen2530: 1411, pplLen30 : 2000},
]
colTitle: { totalLen: "총길이", pplLen2025: "20~25년", pplLen2530: "25~30년", pplLen30: "30년 이상" }, //헤더 영역
data: [
{ name: "금강", totalLen: 4545, pplLen2025: 1140, pplLen2530: 1391, pplLen30: 2014 },
{ name: "한강", totalLen: 17099, pplLen2025: 3181, pplLen2530: 3615, pplLen30: 10303 },
{ name: "낙동강", totalLen: 14745, pplLen2025: 3100, pplLen2530: 1814, pplLen30: 9831 },
{ name: "영산강", totalLen: 5073, pplLen2025: 1662, pplLen2530: 1411, pplLen30: 2000 },
]
};
type mapPplOldState = {
mapData: mapPplOldItem[];
@@ -136,7 +136,7 @@ export class SewerageApiService implements SewerageService {
...item,
sumPrlgVl: Number(item.sumPrlgVl),
}));
return list;
}
@@ -158,6 +158,32 @@ export class SewerageApiService implements SewerageService {
}));
return list;
}
// 유역별 용도별 관로 길이(km) MV조회 : usgCd - SBA004 오수관, SBA003 우수관
public async getMvUsgPplLength(): Promise<PplLengthContent[]> {
const response = await client.get<{
PplLengthList: PplLengthContent[];
}>(ENDPOINT.SEWERAGE_SERVICE.MV_USG_PPL_LENGTH);
const list = (response.data.PplLengthList ?? []).map((item) => ({
...item,
sumPrlgVl: Number(item.sumPrlgVl),
}));
return list;
}
// 유역별 노후도별 관로 길이(km) MV조회 : yearGroup - 20년~25년미만,25년~30년미만,30년이상
public async getMvOldPplLength(): Promise<PplLengthContent[]> {
const response = await client.get<{
PplLengthList: PplLengthContent[];
}>(ENDPOINT.SEWERAGE_SERVICE.MV_OLD_PPL_LENGTH);
const list = (response.data.PplLengthList ?? []).map((item) => ({
...item,
sumPrlgVl: Number(item.sumPrlgVl),
}));
return list;
}
}
export const SewerageApi = new SewerageApiService();
@@ -2,11 +2,15 @@ import { basinKeyMap } from "../model/map-data";
export interface PplLengthContent {
zoneNm: string;
sumPrlgVl: number;
usgCd?: string;
yearGroup?: string;
}
export interface SewerageService {
getPplLength(ymdFrom: string, ymdTo: string, useCd: string): Promise<PplLengthContent[]>;
getPplUseLength(ymdFrom: string, ymdTo: string, useCd: string): Promise<PplLengthContent[]>;
getMvUsgPplLength(): Promise<PplLengthContent[]>;
getMvOldPplLength(): Promise<PplLengthContent[]>;
}
export interface ChartDataSet {
@@ -39,11 +43,11 @@ export type mapPplOldItem = {
};
export type mapPplOldType = {
colTitle: {
colTitle: {
totalLen: string; // 총길이
pplLen2025: string; // 20~25년
pplLen2530: string; // 25~30년
pplLen30 : string; // 30년 이상
pplLen30: string; // 30년 이상
};
data: mapPplOldItem[];
};
@@ -72,7 +76,7 @@ export function buildSnOldPplLength(
colTitle,
rows: [
total, // 총길이 (undefined면 0)
...datasetValues, // 각 연차 데이터 (undefined면 0)
...datasetValues, // 각 연차 데이터 (undefined면 0)
],
};
});
+40 -39
View File
@@ -23,55 +23,56 @@ import { SewerageApi, useOldPplStore,useMapOldPplStore, transformChartDataToMapD
ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend);
export default function SnOldPpl() {
const [oldPpl2025Data, setOldPpl2025Data] = useState<PplLengthContent[]>([]);
const [oldPpl2530Data, setOldPpl2530Data] = useState<PplLengthContent[]>([]);
const [oldPpl30Data, setOldPpl30Data] = useState<PplLengthContent[]>([]);
const [oldPplLenData, setOldPplLenData] = useState<PplLengthContent[]>([]);
const {chartData, setChartData} = useOldPplStore();
const {setMapData} = useMapOldPplStore();
useEffect(() => {
const currentYear = new Date().getFullYear();
const ranges = {
"20-25년": { from: `${currentYear - 25 + 1}0101`, to: `${currentYear - 20}1231`, setter: setOldPpl2025Data },
"25-30년": { from: `${currentYear - 30 + 1}0101`, to: `${currentYear - 25}1231`, setter: setOldPpl2530Data },
"30년이상": { from: "19010101", to: `${currentYear - 30}1231`, setter: setOldPpl30Data },
};
Object.entries(ranges).forEach(([label, range]) => {
SewerageApi.getPplLength(range.from, range.to)
.then(range.setter)
.catch((e) => console.error(`${label} 데이터 조회 실패`, e));
});
// 유역별, 노후년도별 관로길이 데이터를 한번에 조회
SewerageApi.getMvOldPplLength()
.then((apiResult) => {
setOldPplLenData(apiResult);
})
.catch((e) => console.error("노후관로 데이터 조회 실패", e));
}, []);
useEffect(() => {
const allData = [...oldPpl2025Data, ...oldPpl2530Data, ...oldPpl30Data];
if (allData.length === 0) return;
if (oldPplLenData.length === 0) return;
const allZones = Array.from(new Set(allData.map(d => d.zoneNm)));
// 유역 목록 추출 (중복 제거)
const allZones = [...new Set(oldPplLenData.map(d => d.zoneNm))];
const createDataset = (label: string, data: PplLengthContent[], color: string) => {
const dataMap = new Map(data.map(item => [item.zoneNm, item.sumPrlgVl]));
// console.log(dataMap);
return {
label,
data: allZones.map(zone => dataMap.get(zone) ?? 0),
backgroundColor: color,
borderRadius: 4,
};
// yearGroup 매핑: API 응답값 -> 차트 label
const yearGroupMap: { [key: string]: { label: string; color: string } } = {
"20년~25년미만": { label: "20-25년", color: "#3B82F6" },
"25년~30년미만": { label: "25-30년", color: "#EF4444" },
"30년이상": { label: "30년이상", color: "#F87171" },
};
const newDatasets: ChartDataSet[] = [];
if (oldPpl2025Data.length > 0) {
newDatasets.push(createDataset('20-25년', oldPpl2025Data, '#3B82F6'));
}
if (oldPpl2530Data.length > 0) {
newDatasets.push(createDataset('25-30년', oldPpl2530Data, '#EF4444'));
}
if (oldPpl30Data.length > 0) {
newDatasets.push(createDataset('30년이상', oldPpl30Data, '#F87171'));
}
// 유역별, 노후년도별 데이터 구조화
const dataByZone: { [zone: string]: { [yearGroup: string]: number } } = {};
allZones.forEach(zone => {
dataByZone[zone] = {};
});
oldPplLenData.forEach(item => {
let zoneData = dataByZone[item.zoneNm];
if (!zoneData) {
zoneData = {};
dataByZone[item.zoneNm] = zoneData;
}
if (item.yearGroup) {
zoneData[item.yearGroup] = item.sumPrlgVl;
}
});
// 데이터셋 생성
const newDatasets: ChartDataSet[] = Object.entries(yearGroupMap).map(([yearGroup, { label, color }]) => ({
label,
data: allZones.map(zone => dataByZone[zone]?.[yearGroup] ?? 0),
backgroundColor: color,
borderRadius: 4,
}));
const newChartData: OldPplChartData = {
labels: allZones,
@@ -82,7 +83,7 @@ export default function SnOldPpl() {
// 차트 데이터를 기반으로 지도 팝업 데이터 생성 및 상태 업데이트
setMapData(transformChartDataToMapData(newChartData));
}, [oldPpl2025Data, oldPpl2530Data, oldPpl30Data]);
}, [oldPplLenData]);
// 각 지역별 총 길이 계산
const totalLengths = chartData.labels.map((label, i) => {
@@ -25,81 +25,60 @@ interface ChartDataSet {
borderColor?: string;
}
// const usgCd = { "SBA004": "오수관로", "SBA003": "우수관로", "ETC": "기타" }; // 용도
ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend);
export default function SnPplStatus() {
const [sewageData, setSewageData] = useState<PplLengthContent[]>([]);
const [stormwaterData, setStormwaterData] = useState<PplLengthContent[]>([]);
const [totalData, setTotalData] = useState<PplLengthContent[]>([]);
// const [chartData, setChartData] = useState<{
// labels: string[];
// datasets: ChartDataSet[];
// }>({ labels: [], datasets: [] });
const {chartData, setChartData} = useUsePplStore();
const [usgPplLenData, setUsgPplLenData] = useState<PplLengthContent[]>([]);
useEffect(() => {
const ymdFrom = "19010101";
const ymdTo = "20241231";
SewerageApi.getPplUseLength(ymdFrom, ymdTo, "SBA004")
// 유역별, 용도별 관로길이 데이터를 한번에 조회
SewerageApi.getMvUsgPplLength()
.then((apiResult) => {
// getPplUseLength의 결과(apiResult)를
// setSewageData 함수의 인자로 넣어서 호출합니다.
// console.log(apiResult);
setSewageData(apiResult);
setUsgPplLenData(apiResult);
})
.catch((e) => console.error("오수관 데이터 조회 실패", e));
SewerageApi.getPplUseLength(ymdFrom, ymdTo, "SBA003")
.then(setStormwaterData)
.catch((e) => console.error("우수관 데이터 조회 실패", e));
SewerageApi.getPplLength(ymdFrom, ymdTo)
.then((apiResult) => {
// getPplLength 결과(apiResult)를
// setTotalData 함수의 인자로 넣어서 호출합니다.
// console.log(apiResult);
setTotalData(apiResult);
})
.catch((e) => console.error("전체 관로 데이터 조회 실패", e));
.catch((e) => console.error("용도별 관로 데이터 조회 실패", e));
}, []);
// 데이터가 변경될 때마다 차트를 다시 계산
useEffect(() => {
// 오수관 우수관 두 데이터 모두 없으면 차트를 그리지 않음
if (sewageData.length === 0 && stormwaterData.length === 0) return;
if (usgPplLenData.length === 0) return;
const allZones = new Set([
...sewageData.map(d => d.zoneNm),
...stormwaterData.map(d => d.zoneNm),
...totalData.map(d => d.zoneNm)
]);
const labels = Array.from(allZones);
// 유역 목록 추출 (중복 제거)
const allZones = [...new Set(usgPplLenData.map(d => d.zoneNm))];
const labels = allZones;
const dataByZone: { [key: string]: { total: number; sewage: number; stormwater: number } } = {};
labels.forEach(zone => {dataByZone[zone] = { total: 0, sewage: 0, stormwater: 0 }; });
totalData.forEach(item => {
if (!dataByZone[item.zoneNm]) {
dataByZone[item.zoneNm] = { total: 0, sewage: 0, stormwater: 0 };
}
dataByZone[item.zoneNm]!.total = item.sumPrlgVl;
// 유역별, 용도별 데이터 구조화
const dataByZone: { [key: string]: { sewage: number; stormwater: number; etc: number } } = {};
labels.forEach(zone => {
dataByZone[zone] = { sewage: 0, stormwater: 0, etc: 0 };
});
sewageData.forEach(item => {
// usgCd별로 데이터 분류: SBA004(오수관로), SBA003(우수관로), ETC(기타)
usgPplLenData.forEach(item => {
if (!dataByZone[item.zoneNm]) {
dataByZone[item.zoneNm] = { total: 0, sewage: 0, stormwater: 0 };
dataByZone[item.zoneNm] = { sewage: 0, stormwater: 0, etc: 0 };
}
dataByZone[item.zoneNm]!.sewage = item.sumPrlgVl;
});
stormwaterData.forEach(item => {
if (!dataByZone[item.zoneNm]) {
dataByZone[item.zoneNm] = { total: 0, sewage: 0, stormwater: 0 };
if (item.usgCd === "SBA004") {
dataByZone[item.zoneNm]!.sewage = item.sumPrlgVl;
} else if (item.usgCd === "SBA003") {
dataByZone[item.zoneNm]!.stormwater = item.sumPrlgVl;
} else if (item.usgCd === "ETC") {
dataByZone[item.zoneNm]!.etc = item.sumPrlgVl;
}
dataByZone[item.zoneNm]!.stormwater = item.sumPrlgVl;
});
const datasets: ChartDataSet[] = [];
// 기타 데이터셋 (맨 아래에 쌓기 위해 먼저 추가)
datasets.push({
label: "기타",
data: labels.map(zone => dataByZone[zone]?.etc),
backgroundColor: "rgba(255, 178, 150, 0.9)",
});
// 오수관로 데이터셋
datasets.push({
label: "오수관로",
@@ -114,21 +93,9 @@ export default function SnPplStatus() {
backgroundColor: "rgba(170, 210, 245, 0.95)",
});
// 전체 데이터가 도착했다면 '기타' 데이터셋을 계산하고 추가
if (totalData.length > 0) {
datasets.unshift({ // '기타'를 맨 아래에 쌓기 위해 unshift 사용
label: "기타",
data: labels.map(zone => {
const zoneData = dataByZone[zone] ?? { total: 0, sewage: 0, stormwater: 0 };
return zoneData.total - zoneData.sewage - zoneData.stormwater;
}),
backgroundColor: "rgba(255, 178, 150, 0.9)",
});
}
setChartData({ labels, datasets });
}, [sewageData, stormwaterData, totalData]);
}, [usgPplLenData]);
// 총합(라벨당) 계산
const totals = chartData.labels.map((_, i) =>
+3 -1
View File
@@ -78,7 +78,9 @@ export const ENDPOINT = {
PPL_USE_LENGTH: "/sewerage-service/api/v1/ms/pplUseLength",
PPL_COUNTS: "/sewerage-service/api/v1/ms/pplCounts",
OLD_PPL_COUNTS: "/sewerage-service/api/v1/ms/oldPplCounts",
},
MV_USG_PPL_LENGTH: "/sewerage-service/api/v1/ms/mvUsgPplLength",
MV_OLD_PPL_LENGTH: "/sewerage-service/api/v1/ms/mvOldPplLength",
},
OPERATION_SERVICE: {
MAIN_YEAR: "/operation-service/api/v1/dashBoard/main/year",
OPER_CNTRT: "/operation-service/api/v1/dashBoard/main/oper/cntrt",