feat: 건설현장 관리에서 건설현장 통계에 발주기관 별 건설현장 프로젝트 입력상태 및 기관별 성과 현황 구헌
parent
9e1186280e
commit
bc5d1622a6
|
|
@ -45,4 +45,14 @@ public interface ConstructionProjectManagementMapper {
|
||||||
*/
|
*/
|
||||||
public int selectSiteCountByDistrictCodes(HashMap<String, Object> params) throws SQLException;
|
public int selectSiteCountByDistrictCodes(HashMap<String, Object> params) throws SQLException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [신규] 기관별 입력 상태별(PROJECT_STATE_CODE) 현황 조회
|
||||||
|
*/
|
||||||
|
public List<EgovMap> selectProjectStateCountByDistrictCodes(HashMap<String, Object> params) throws SQLException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [신규] 기관별 성과 현황(CONST_STATE_CODE - 공사단계) 조회
|
||||||
|
*/
|
||||||
|
public List<EgovMap> selectConstStateCountByDistrictCodes(HashMap<String, Object> params) throws SQLException;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -28,14 +28,11 @@ public class ConstructionProjectManagementServiceImpl implements ConstructionPro
|
||||||
@Autowired
|
@Autowired
|
||||||
private DrillingInputService drillingInputService;
|
private DrillingInputService drillingInputService;
|
||||||
|
|
||||||
/**
|
|
||||||
* 건설현장 통계 대시보드 데이터 조회
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Object> selectConstructionProjectStatistics(HashMap<String, Object> params) throws Exception {
|
public Map<String, Object> selectConstructionProjectStatistics(HashMap<String, Object> params) throws Exception {
|
||||||
Map<String, Object> resultMap = new HashMap<>();
|
Map<String, Object> resultMap = new HashMap<>();
|
||||||
|
|
||||||
// 1. 사용자 권한 및 조직 코드 설정 (기존 로직)
|
// 1. 사용자 권한 및 조직 코드 설정
|
||||||
String userId = MyUtil.getStringFromObject(params.get("userId"));
|
String userId = MyUtil.getStringFromObject(params.get("userId"));
|
||||||
if (userId != null && !userId.isEmpty()) {
|
if (userId != null && !userId.isEmpty()) {
|
||||||
HashMap<String, Object> orgCodes = drillingInputService.getOrganizationUserGlGmGsGfCodes(userId);
|
HashMap<String, Object> orgCodes = drillingInputService.getOrganizationUserGlGmGsGfCodes(userId);
|
||||||
|
|
@ -53,99 +50,129 @@ public class ConstructionProjectManagementServiceImpl implements ConstructionPro
|
||||||
resultMap.put("totalCount", totalCount);
|
resultMap.put("totalCount", totalCount);
|
||||||
|
|
||||||
// =================================================================
|
// =================================================================
|
||||||
// [기관별 통계 로직] v_gl='01' 필터링 및 코드순 정렬
|
// [기관별 통계 로직] 데이터 합산 및 정렬
|
||||||
// =================================================================
|
// =================================================================
|
||||||
List<Map<String, Object>> institutionStats = new ArrayList<>();
|
// 중복 기관 합산을 위한 맵 (Key: 기관명)
|
||||||
|
Map<String, Map<String, Object>> uniqueStatsMap = new HashMap<>();
|
||||||
|
|
||||||
// A. 통계 대상 기관명 목록 조회
|
|
||||||
List<String> companyList = constructionProjectManagementMapper.selectStatTargetCompanies();
|
List<String> companyList = constructionProjectManagementMapper.selectStatTargetCompanies();
|
||||||
|
|
||||||
// 부산 지역(v_gl='01') 합계용 변수
|
|
||||||
int busanTotalCount = 0;
|
int busanTotalCount = 0;
|
||||||
|
|
||||||
if (companyList != null) {
|
if (companyList != null) {
|
||||||
for (String companyName : companyList) {
|
for (String companyName : companyList) {
|
||||||
if (companyName == null || companyName.trim().isEmpty()) continue;
|
if (companyName == null || companyName.trim().isEmpty()) continue;
|
||||||
|
|
||||||
// B. SP 호출하여 지역코드(GL, GM, GS) 획득
|
|
||||||
HashMap<String, Object> spParams = new HashMap<>();
|
HashMap<String, Object> spParams = new HashMap<>();
|
||||||
spParams.put("projectMasterCompanyName", companyName);
|
spParams.put("projectMasterCompanyName", companyName);
|
||||||
|
|
||||||
// SP 실행 (OUT 변수는 spParams에 담김: v_gl, v_gm, v_gs)
|
|
||||||
constructionProjectManagementMapper.spGetMasterCompanyDistrict(spParams);
|
constructionProjectManagementMapper.spGetMasterCompanyDistrict(spParams);
|
||||||
|
|
||||||
String v_gl = (String) spParams.get("v_gl");
|
String v_gl = (String) spParams.get("v_gl");
|
||||||
String v_gm = (String) spParams.get("v_gm");
|
String v_gm = (String) spParams.get("v_gm");
|
||||||
String v_gs = (String) spParams.get("v_gs");
|
String v_gs = (String) spParams.get("v_gs");
|
||||||
|
|
||||||
// [필터링 조건] v_gl이 '01'인 경우에만 처리
|
// [필터링] v_gl이 '01'인 경우에만 처리
|
||||||
if ("01".equals(v_gl)) {
|
if ("01".equals(v_gl)) {
|
||||||
|
|
||||||
// C. 해당 지역코드로 건설현장 수 카운트
|
// [명칭 보정] 한국도로공사 코드인 경우 이름을 강제 통일 (울산광역시 -> 한국도로공사 등)
|
||||||
HashMap<String, Object> countParams = new HashMap<>();
|
if ("01".equals(v_gl) && "02".equals(v_gm) && "002".equals(v_gs)) {
|
||||||
countParams.put("v_gl", v_gl);
|
companyName = "한국도로공사";
|
||||||
countParams.put("v_gm", v_gm);
|
}
|
||||||
countParams.put("v_gs", v_gs);
|
|
||||||
|
|
||||||
int siteCount = constructionProjectManagementMapper.selectSiteCountByDistrictCodes(countParams);
|
HashMap<String, Object> queryParams = new HashMap<>();
|
||||||
|
queryParams.put("v_gl", v_gl);
|
||||||
|
queryParams.put("v_gm", v_gm);
|
||||||
|
queryParams.put("v_gs", v_gs);
|
||||||
|
|
||||||
|
// 1) 전체 건수
|
||||||
|
int siteCount = constructionProjectManagementMapper.selectSiteCountByDistrictCodes(queryParams);
|
||||||
|
|
||||||
// 그래프용 데이터 저장 (기관명, 건수, 정렬용 코드들)
|
// 2) 입력 상태별 현황
|
||||||
Map<String, Object> stat = new HashMap<>();
|
List<EgovMap> inputStateList = constructionProjectManagementMapper.selectProjectStateCountByDistrictCodes(queryParams);
|
||||||
stat.put("name", companyName);
|
int[] inputStatusData = new int[7];
|
||||||
stat.put("count", siteCount);
|
for(EgovMap m : inputStateList) {
|
||||||
stat.put("gl", v_gl);
|
int code = MyUtil.getIntegerFromObject(m.get("code"));
|
||||||
stat.put("gm", v_gm);
|
int cnt = MyUtil.getIntegerFromObject(m.get("cnt"));
|
||||||
stat.put("gs", v_gs);
|
if(code >= 0 && code <= 6) inputStatusData[code] = cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3) 성과 현황
|
||||||
|
List<EgovMap> perfStateList = constructionProjectManagementMapper.selectConstStateCountByDistrictCodes(queryParams);
|
||||||
|
int[] performanceData = new int[6];
|
||||||
|
for(EgovMap m : perfStateList) {
|
||||||
|
int code = MyUtil.getIntegerFromObject(m.get("code"));
|
||||||
|
int cnt = MyUtil.getIntegerFromObject(m.get("cnt"));
|
||||||
|
if(code >= 1 && code <= 6) performanceData[code - 1] = cnt;
|
||||||
|
}
|
||||||
|
|
||||||
institutionStats.add(stat);
|
// [데이터 합산 로직] 이미 존재하는 기관명이면 데이터 누적
|
||||||
|
if (uniqueStatsMap.containsKey(companyName)) {
|
||||||
|
Map<String, Object> existing = uniqueStatsMap.get(companyName);
|
||||||
|
|
||||||
|
// 건수 합산
|
||||||
|
int currentCount = (Integer) existing.get("count");
|
||||||
|
existing.put("count", currentCount + siteCount);
|
||||||
|
|
||||||
|
// 입력상태 배열 합산
|
||||||
|
int[] exInput = (int[]) existing.get("inputStatusData");
|
||||||
|
for(int i=0; i<7; i++) exInput[i] += inputStatusData[i];
|
||||||
|
existing.put("inputStatusData", exInput);
|
||||||
|
|
||||||
|
// 성과현황 배열 합산
|
||||||
|
int[] exPerf = (int[]) existing.get("performanceData");
|
||||||
|
for(int i=0; i<6; i++) exPerf[i] += performanceData[i];
|
||||||
|
existing.put("performanceData", exPerf);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// 신규 추가
|
||||||
|
Map<String, Object> stat = new HashMap<>();
|
||||||
|
stat.put("name", companyName);
|
||||||
|
stat.put("count", siteCount);
|
||||||
|
stat.put("gl", v_gl);
|
||||||
|
stat.put("gm", v_gm);
|
||||||
|
stat.put("gs", v_gs);
|
||||||
|
stat.put("inputStatusData", inputStatusData);
|
||||||
|
stat.put("performanceData", performanceData);
|
||||||
|
|
||||||
|
uniqueStatsMap.put(companyName, stat);
|
||||||
|
}
|
||||||
|
|
||||||
// 부산 지역 합계 누적
|
|
||||||
busanTotalCount += siteCount;
|
busanTotalCount += siteCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// [정렬] GL_CODE ASC, GM_CODE ASC, GS_CODE ASC 순서로 정렬
|
|
||||||
Collections.sort(institutionStats, new Comparator<Map<String, Object>>() {
|
|
||||||
@Override
|
|
||||||
public int compare(Map<String, Object> o1, Map<String, Object> o2) {
|
|
||||||
String gl1 = (String) o1.get("gl");
|
|
||||||
String gl2 = (String) o2.get("gl");
|
|
||||||
int result = compareString(gl1, gl2);
|
|
||||||
if (result != 0) return result;
|
|
||||||
|
|
||||||
String gm1 = (String) o1.get("gm");
|
|
||||||
String gm2 = (String) o2.get("gm");
|
|
||||||
result = compareString(gm1, gm2);
|
|
||||||
if (result != 0) return result;
|
|
||||||
|
|
||||||
String gs1 = (String) o1.get("gs");
|
|
||||||
String gs2 = (String) o2.get("gs");
|
|
||||||
return compareString(gs1, gs2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// null-safe string comparison
|
|
||||||
private int compareString(String s1, String s2) {
|
|
||||||
if (s1 == null) s1 = "";
|
|
||||||
if (s2 == null) s2 = "";
|
|
||||||
return s1.compareTo(s2);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 결과 맵에 저장 (정렬된 리스트)
|
// Map 값을 List로 변환
|
||||||
|
List<Map<String, Object>> institutionStats = new ArrayList<>(uniqueStatsMap.values());
|
||||||
|
|
||||||
|
// [정렬] GL_CODE ASC, GM_CODE ASC, GS_CODE ASC 순서로 정렬
|
||||||
|
Collections.sort(institutionStats, new Comparator<Map<String, Object>>() {
|
||||||
|
@Override
|
||||||
|
public int compare(Map<String, Object> o1, Map<String, Object> o2) {
|
||||||
|
String gl1 = (String) o1.get("gl"); String gl2 = (String) o2.get("gl");
|
||||||
|
int result = compareString(gl1, gl2); if (result != 0) return result;
|
||||||
|
|
||||||
|
String gm1 = (String) o1.get("gm"); String gm2 = (String) o2.get("gm");
|
||||||
|
result = compareString(gm1, gm2); if (result != 0) return result;
|
||||||
|
|
||||||
|
String gs1 = (String) o1.get("gs"); String gs2 = (String) o2.get("gs");
|
||||||
|
return compareString(gs1, gs2);
|
||||||
|
}
|
||||||
|
private int compareString(String s1, String s2) {
|
||||||
|
return (s1 == null ? "" : s1).compareTo(s2 == null ? "" : s2);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
resultMap.put("institutionStats", institutionStats);
|
resultMap.put("institutionStats", institutionStats);
|
||||||
resultMap.put("busanCount", busanTotalCount);
|
resultMap.put("busanCount", busanTotalCount);
|
||||||
|
|
||||||
|
|
||||||
// 3. 지역별 통계 조회
|
// 3. 지역별 통계 조회
|
||||||
List<EgovMap> regionList = constructionProjectManagementMapper.selectRegionCount(params);
|
List<EgovMap> regionList = constructionProjectManagementMapper.selectRegionCount(params);
|
||||||
resultMap.put("regionList", regionList);
|
resultMap.put("regionList", regionList);
|
||||||
|
|
||||||
// 4. 단계별 통계 조회
|
// 4. 단계별 통계
|
||||||
List<EgovMap> stageList = constructionProjectManagementMapper.selectStageCount(params);
|
List<EgovMap> stageList = constructionProjectManagementMapper.selectStageCount(params);
|
||||||
Map<String, Integer> stageCounts = new HashMap<>();
|
Map<String, Integer> stageCounts = new HashMap<>();
|
||||||
|
|
||||||
// 초기화
|
|
||||||
stageCounts.put("feasibility", 0);
|
stageCounts.put("feasibility", 0);
|
||||||
stageCounts.put("basicDesign", 0);
|
stageCounts.put("basicDesign", 0);
|
||||||
stageCounts.put("detailDesign", 0);
|
stageCounts.put("detailDesign", 0);
|
||||||
|
|
@ -169,7 +196,6 @@ public class ConstructionProjectManagementServiceImpl implements ConstructionPro
|
||||||
// 5. 최근 입력된 건설현장
|
// 5. 최근 입력된 건설현장
|
||||||
params.put("firstIndex", 0);
|
params.put("firstIndex", 0);
|
||||||
params.put("recordCountPerPage", 5);
|
params.put("recordCountPerPage", 5);
|
||||||
|
|
||||||
List<EgovMap> recentList = constructionProjectManagementMapper.selectRecentProjects(params);
|
List<EgovMap> recentList = constructionProjectManagementMapper.selectRecentProjects(params);
|
||||||
resultMap.put("recentList", recentList);
|
resultMap.put("recentList", recentList);
|
||||||
|
|
||||||
|
|
@ -180,5 +206,4 @@ public class ConstructionProjectManagementServiceImpl implements ConstructionPro
|
||||||
|
|
||||||
return resultMap;
|
return resultMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?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">
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
|
||||||
<!-- namespace 확인: Java Interface 경로와 일치 -->
|
|
||||||
<mapper namespace="geoinfo.admins.constructionProjectManagement.service.ConstructionProjectManagementMapper">
|
<mapper namespace="geoinfo.admins.constructionProjectManagement.service.ConstructionProjectManagementMapper">
|
||||||
|
|
||||||
<!-- [공통] 검색 조건 Fragment (기존 유지) -->
|
<!-- [공통] 검색 조건 Fragment -->
|
||||||
<sql id="searchConditions">
|
<sql id="searchConditions">
|
||||||
<if test="constName != null and constName != ''">
|
<if test="constName != null and constName != ''">
|
||||||
AND A.CONST_NAME LIKE '%' || #{constName} || '%'
|
AND A.CONST_NAME LIKE '%' || #{constName} || '%'
|
||||||
|
|
@ -26,7 +25,7 @@
|
||||||
</if>
|
</if>
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
<!-- 1. 전체 등록 수 조회 (기존 유지) -->
|
<!-- 1. 전체 등록 수 조회 -->
|
||||||
<select id="selectTotalCount" parameterType="hashmap" resultType="long">
|
<select id="selectTotalCount" parameterType="hashmap" resultType="long">
|
||||||
SELECT COUNT(*)
|
SELECT COUNT(*)
|
||||||
FROM TEMP_CONSTRUCT_SITE_INFO A
|
FROM TEMP_CONSTRUCT_SITE_INFO A
|
||||||
|
|
@ -34,7 +33,7 @@
|
||||||
<include refid="searchConditions" />
|
<include refid="searchConditions" />
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<!-- 2. 지역별 통계 조회 (기존 유지) -->
|
<!-- 2. 지역별 통계 조회 -->
|
||||||
<select id="selectRegionCount" parameterType="hashmap" resultType="egovMap">
|
<select id="selectRegionCount" parameterType="hashmap" resultType="egovMap">
|
||||||
SELECT
|
SELECT
|
||||||
SUBSTR(B.PROJECT_START_SPOT, 1, 2) AS REGION_NAME,
|
SUBSTR(B.PROJECT_START_SPOT, 1, 2) AS REGION_NAME,
|
||||||
|
|
@ -47,7 +46,7 @@
|
||||||
GROUP BY SUBSTR(B.PROJECT_START_SPOT, 1, 2)
|
GROUP BY SUBSTR(B.PROJECT_START_SPOT, 1, 2)
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<!-- 3. 단계별 통계 조회 (기존 유지) -->
|
<!-- 3. 단계별 통계 조회 (전체) -->
|
||||||
<select id="selectStageCount" parameterType="hashmap" resultType="egovMap">
|
<select id="selectStageCount" parameterType="hashmap" resultType="egovMap">
|
||||||
SELECT
|
SELECT
|
||||||
CONST_STATE_CODE AS "constStateCode",
|
CONST_STATE_CODE AS "constStateCode",
|
||||||
|
|
@ -58,7 +57,7 @@
|
||||||
GROUP BY CONST_STATE_CODE
|
GROUP BY CONST_STATE_CODE
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<!-- 4. 최근 입력된 건설현장 목록 조회 (기존 유지) -->
|
<!-- 4. 최근 입력된 건설현장 목록 조회 -->
|
||||||
<select id="selectRecentProjects" parameterType="hashmap" resultType="egovMap">
|
<select id="selectRecentProjects" parameterType="hashmap" resultType="egovMap">
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM (
|
FROM (
|
||||||
|
|
@ -83,7 +82,7 @@
|
||||||
WHERE RN BETWEEN #{firstIndex} + 1 AND #{firstIndex} + #{recordCountPerPage}
|
WHERE RN BETWEEN #{firstIndex} + 1 AND #{firstIndex} + #{recordCountPerPage}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<!-- 5. [수정] 통계 대상 기관명 목록 조회 (사용자 요청 쿼리 적용) -->
|
<!-- 5. 통계 대상 기관명 목록 조회 -->
|
||||||
<select id="selectStatTargetCompanies" resultType="string">
|
<select id="selectStatTargetCompanies" resultType="string">
|
||||||
SELECT tmc.COM_NAME
|
SELECT tmc.COM_NAME
|
||||||
FROM (
|
FROM (
|
||||||
|
|
@ -112,7 +111,7 @@
|
||||||
)}
|
)}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<!-- 7. [수정] 지역코드로 건설현장 수 구하기 (사용자 요청 매핑 적용) -->
|
<!-- 7. 지역코드로 건설현장 수 구하기 -->
|
||||||
<select id="selectSiteCountByDistrictCodes" parameterType="hashmap" resultType="int">
|
<select id="selectSiteCountByDistrictCodes" parameterType="hashmap" resultType="int">
|
||||||
SELECT COUNT(*)
|
SELECT COUNT(*)
|
||||||
FROM TEMP_CONSTRUCT_SITE_INFO
|
FROM TEMP_CONSTRUCT_SITE_INFO
|
||||||
|
|
@ -123,4 +122,30 @@
|
||||||
<!-- AND CONST_TAG = 'Y' -->
|
<!-- AND CONST_TAG = 'Y' -->
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<!-- [수정] 8. 기관별 입력 상태별(PROJECT_STATE_CODE) 현황 조회 -->
|
||||||
|
<!-- CONST_TAG = 'Y' 조건 제거 -->
|
||||||
|
<select id="selectProjectStateCountByDistrictCodes" parameterType="hashmap" resultType="egovMap">
|
||||||
|
SELECT
|
||||||
|
PROJECT_STATE_CODE AS "code",
|
||||||
|
COUNT(*) AS "cnt"
|
||||||
|
FROM TEMP_CONSTRUCT_SITE_INFO
|
||||||
|
WHERE MASTER_COMPANY_O_CODE = #{v_gl}
|
||||||
|
AND MASTER_COMPANY_TW_CODE = #{v_gm}
|
||||||
|
AND MASTER_COMPANY_TH_CODE = #{v_gs}
|
||||||
|
GROUP BY PROJECT_STATE_CODE
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- [수정] 9. 기관별 성과 현황(CONST_STATE_CODE) 조회 -->
|
||||||
|
<!-- CONST_TAG = 'Y' 조건 제거 -->
|
||||||
|
<select id="selectConstStateCountByDistrictCodes" parameterType="hashmap" resultType="egovMap">
|
||||||
|
SELECT
|
||||||
|
CONST_STATE_CODE AS "code",
|
||||||
|
COUNT(*) AS "cnt"
|
||||||
|
FROM TEMP_CONSTRUCT_SITE_INFO
|
||||||
|
WHERE MASTER_COMPANY_O_CODE = #{v_gl}
|
||||||
|
AND MASTER_COMPANY_TW_CODE = #{v_gm}
|
||||||
|
AND MASTER_COMPANY_TH_CODE = #{v_gs}
|
||||||
|
GROUP BY CONST_STATE_CODE
|
||||||
|
</select>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|
@ -12,32 +12,56 @@
|
||||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
|
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
|
||||||
<link rel="stylesheet" HREF="${pageContext.request.contextPath}/css/admins/style.css" type="text/css">
|
<link rel="stylesheet" HREF="${pageContext.request.contextPath}/css/admins/style.css" type="text/css">
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* 그래프 컨테이너 스타일 */
|
||||||
|
.chart-wrapper {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding: 20px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
||||||
|
}
|
||||||
|
.chart-header {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
border-bottom: 2px solid #c87202;
|
||||||
|
}
|
||||||
|
.sub-chart-title {
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
.sub-chart-box {
|
||||||
|
height: 300px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var context = "${pageContext.request.contextPath}";
|
var context = "${pageContext.request.contextPath}";
|
||||||
var myChartInstance = null; // 단계별 차트 인스턴스
|
var myChartInstance = null; // 상단 전체 단계별 차트
|
||||||
var instChartInstance = null; // 기관별 차트 인스턴스 (신규)
|
var instChartInstance = null; // 하단 전체 기관별 차트
|
||||||
|
|
||||||
// [1] 문서 로드 완료 시 초기 실행
|
// [1] 문서 로드 완료 시 초기 실행 (Vanilla JS)
|
||||||
document.addEventListener("DOMContentLoaded", function() {
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
// 초기 로딩 시 검색 조건 없이 전체 통계 조회
|
|
||||||
moveConstructionUserDetail();
|
moveConstructionUserDetail();
|
||||||
});
|
});
|
||||||
|
|
||||||
// [2] 통계 조회 함수 (Vanilla JS)
|
// [2] 통계 조회 함수 (Vanilla JS)
|
||||||
function moveConstructionUserDetail() {
|
function moveConstructionUserDetail() {
|
||||||
// 기본 검색 조건 (필요시 변경 가능)
|
var params = { constTag: "Y" };
|
||||||
var params = {
|
|
||||||
constTag: "Y"
|
|
||||||
};
|
|
||||||
|
|
||||||
// AJAX 요청 (XMLHttpRequest 사용)
|
|
||||||
var xhr = new XMLHttpRequest();
|
var xhr = new XMLHttpRequest();
|
||||||
xhr.open("POST", context + "/admins/constructionProjectManagement/selectStatistics.do", true);
|
xhr.open("POST", context + "/admins/constructionProjectManagement/selectStatistics.do", true);
|
||||||
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
|
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
|
||||||
|
|
||||||
xhr.onreadystatechange = function() {
|
xhr.onreadystatechange = function() {
|
||||||
if (xhr.readyState === 4) { // 요청 완료
|
if (xhr.readyState === 4) {
|
||||||
if (xhr.status === 200) { // 성공
|
if (xhr.status === 200) {
|
||||||
try {
|
try {
|
||||||
var res = JSON.parse(xhr.responseText);
|
var res = JSON.parse(xhr.responseText);
|
||||||
if(res.result == "true" || res.result == true) {
|
if(res.result == "true" || res.result == true) {
|
||||||
|
|
@ -56,64 +80,28 @@
|
||||||
xhr.send(JSON.stringify(params));
|
xhr.send(JSON.stringify(params));
|
||||||
}
|
}
|
||||||
|
|
||||||
// [3] 화면 갱신 함수
|
// [3] 화면 갱신 메인 함수
|
||||||
function renderStatistics(data) {
|
function renderStatistics(data) {
|
||||||
// (1) 전체 등록 수
|
// (1) 상단 요약 수치 갱신 (Vanilla JS)
|
||||||
var totalCountElem = document.getElementById("total-count");
|
var totalElem = document.getElementById("total-count");
|
||||||
if(totalCountElem) totalCountElem.textContent = (data.totalCount || 0) + " 건";
|
if (totalElem) totalElem.textContent = (data.totalCount || 0) + " 건";
|
||||||
|
|
||||||
// (2) 지역별 통계
|
|
||||||
var busanElem = document.getElementById("busan-count");
|
var busanElem = document.getElementById("busan-count");
|
||||||
var daeguElem = document.getElementById("daegu-count");
|
if (data.busanCount !== undefined && busanElem) {
|
||||||
var sejongElem = document.getElementById("sejong-count");
|
busanElem.textContent = data.busanCount + " 건";
|
||||||
|
}
|
||||||
|
|
||||||
if(busanElem) busanElem.textContent = "0 건";
|
// (2) 상단 전체 단계별 차트 갱신
|
||||||
if(daeguElem) daeguElem.textContent = "0 건";
|
|
||||||
if(sejongElem) sejongElem.textContent = "0 건";
|
|
||||||
|
|
||||||
// 서버에서 계산된 부산 카운트가 있으면 우선 적용
|
|
||||||
if(data.busanCount !== undefined && busanElem) {
|
|
||||||
busanElem.textContent = data.busanCount + " 건";
|
|
||||||
}
|
|
||||||
|
|
||||||
// 기존 리스트 기반 지역 카운트 (필요 시 사용)
|
|
||||||
if(data.regionList) {
|
|
||||||
data.regionList.forEach(function(item) {
|
|
||||||
var region = item.regionName || item.REGION_NAME || "";
|
|
||||||
var count = item.cnt || item.CNT || 0;
|
|
||||||
|
|
||||||
// 부산은 위에서 처리했으므로 제외하거나 중복 처리 가능
|
|
||||||
if(region.indexOf("대구") >= 0 && daeguElem) daeguElem.textContent = count + " 건";
|
|
||||||
else if(region.indexOf("세종") >= 0 && sejongElem) sejongElem.textContent = count + " 건";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// (3) 최근 입력된 건설현장
|
|
||||||
var recentArea = document.getElementById("recent-project-area");
|
|
||||||
var recentHtml = '<p class="fw-bold" style="font-size: 18px; color:#c87202;">최근 입력된 건설현장</p>';
|
|
||||||
|
|
||||||
if(data.recentList && data.recentList.length > 0) {
|
|
||||||
data.recentList.forEach(function(project) {
|
|
||||||
var name = project.constName || project.CONST_NAME || "";
|
|
||||||
var spot = project.projectStartSpot || project.PROJECT_START_SPOT || "";
|
|
||||||
var spotShort = spot.split(" ")[0];
|
|
||||||
recentHtml += '<p>' + name + ' - ' + spotShort + '</p>';
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
recentHtml += '<p>최근 등록된 데이터가 없습니다.</p>';
|
|
||||||
}
|
|
||||||
recentHtml += '<div class="d-flex justify-content-between align-items-center">' +
|
|
||||||
'<div class="btn-group"><button type="button" class="btn btn-sm btn-outline-secondary">+ 더 보기</button></div></div>';
|
|
||||||
if(recentArea) recentArea.innerHTML = recentHtml;
|
|
||||||
|
|
||||||
// (4) 단계별 건수 차트
|
|
||||||
updateStageChart(data.stageCounts || {});
|
updateStageChart(data.stageCounts || {});
|
||||||
|
|
||||||
|
// (3) [신규] 기관별 상세 그래프 동적 생성 (입력상태, 성과현황)
|
||||||
|
createInstitutionDetailCharts(data.institutionStats || []);
|
||||||
|
|
||||||
// (5) [신규] 기관별 등록 건수 차트 업데이트
|
// (4) 하단 전체 기관 등록 건수 차트 갱신
|
||||||
updateInstitutionChart(data.institutionStats || []);
|
updateInstitutionChart(data.institutionStats || []);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 단계별 차트 그리기
|
// [상단] 전체 단계별 차트
|
||||||
function updateStageChart(stageCounts) {
|
function updateStageChart(stageCounts) {
|
||||||
var chartData = [
|
var chartData = [
|
||||||
stageCounts.feasibility || 0,
|
stageCounts.feasibility || 0,
|
||||||
|
|
@ -123,16 +111,17 @@
|
||||||
stageCounts.completion || 0,
|
stageCounts.completion || 0,
|
||||||
stageCounts.maintenance || 0
|
stageCounts.maintenance || 0
|
||||||
];
|
];
|
||||||
|
|
||||||
var stageFeasElem = document.getElementById("stage-feasibility");
|
|
||||||
if(stageFeasElem) stageFeasElem.textContent = (stageCounts.feasibility || 0) + "건";
|
|
||||||
|
|
||||||
var ctx = document.getElementById('myChart');
|
// 텍스트 업데이트 (Vanilla JS)
|
||||||
if(!ctx) return;
|
var stageElem = document.getElementById("stage-feasibility");
|
||||||
|
if (stageElem) stageElem.textContent = (stageCounts.feasibility || 0) + "건";
|
||||||
|
|
||||||
|
var canvas = document.getElementById('myChart');
|
||||||
|
if(!canvas) return;
|
||||||
|
|
||||||
if (myChartInstance) myChartInstance.destroy();
|
if (myChartInstance) myChartInstance.destroy();
|
||||||
|
|
||||||
myChartInstance = new Chart(ctx, {
|
myChartInstance = new Chart(canvas, {
|
||||||
type: 'bar',
|
type: 'bar',
|
||||||
data: {
|
data: {
|
||||||
labels: ['타당성조사', '기본설계', '실시설계', '시공중', '준공', '유지보수'],
|
labels: ['타당성조사', '기본설계', '실시설계', '시공중', '준공', '유지보수'],
|
||||||
|
|
@ -148,38 +137,97 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// [신규] 기관별 차트 그리기 함수
|
// [중단] 기관별 상세 차트 동적 생성 함수
|
||||||
function updateInstitutionChart(instStats) {
|
function createInstitutionDetailCharts(instStats) {
|
||||||
var ctx = document.getElementById('institutionChart');
|
var container = document.getElementById("institution-detail-area");
|
||||||
if(!ctx) return;
|
if (!container) return;
|
||||||
|
|
||||||
if (instChartInstance) instChartInstance.destroy();
|
|
||||||
|
|
||||||
var labels = [];
|
|
||||||
var data = [];
|
|
||||||
|
|
||||||
// 데이터 분리 및 라벨 보정
|
container.innerHTML = ""; // 기존 내용 초기화
|
||||||
instStats.forEach(function(stat) {
|
|
||||||
var name = stat.name;
|
|
||||||
|
|
||||||
// [라벨 보정] '울산광역시' -> '한국도로공사'
|
|
||||||
if (name === '울산광역시') {
|
|
||||||
name = '한국도로공사';
|
|
||||||
}
|
|
||||||
|
|
||||||
labels.push(name);
|
|
||||||
data.push(stat.count);
|
|
||||||
});
|
|
||||||
|
|
||||||
instChartInstance = new Chart(ctx, {
|
instStats.forEach(function(stat, index) {
|
||||||
type: 'bar', // 막대 그래프
|
// 라벨 보정
|
||||||
|
var name = stat.name;
|
||||||
|
if (name === '울산광역시') name = '한국도로공사';
|
||||||
|
|
||||||
|
// Canvas ID 생성
|
||||||
|
var inputChartId = 'inputChart_' + index;
|
||||||
|
var perfChartId = 'perfChart_' + index;
|
||||||
|
|
||||||
|
// HTML 구조 생성
|
||||||
|
var html = '';
|
||||||
|
html += '<div class="col-sm-12">';
|
||||||
|
html += ' <div class="chart-wrapper">';
|
||||||
|
html += ' <div class="chart-header">' + name + ' (총 ' + stat.count + '건)</div>';
|
||||||
|
html += ' <div class="row">';
|
||||||
|
html += ' <div class="col-sm-6">';
|
||||||
|
html += ' <div class="sub-chart-title">건설현장 프로젝트 입력상태</div>';
|
||||||
|
html += ' <div class="sub-chart-box"><canvas id="' + inputChartId + '"></canvas></div>';
|
||||||
|
html += ' </div>';
|
||||||
|
html += ' <div class="col-sm-6">';
|
||||||
|
html += ' <div class="sub-chart-title">기관별 성과 현황</div>';
|
||||||
|
html += ' <div class="sub-chart-box"><canvas id="' + perfChartId + '"></canvas></div>';
|
||||||
|
html += ' </div>';
|
||||||
|
html += ' </div>';
|
||||||
|
html += ' </div>';
|
||||||
|
html += '</div>';
|
||||||
|
|
||||||
|
// DOM에 추가
|
||||||
|
var div = document.createElement("div");
|
||||||
|
div.className = "row"; // 행 단위로 추가
|
||||||
|
div.innerHTML = html;
|
||||||
|
container.appendChild(div);
|
||||||
|
|
||||||
|
// 1) 입력상태 파이 차트 그리기
|
||||||
|
drawPieChart(inputChartId, stat.inputStatusData);
|
||||||
|
|
||||||
|
// 2) 성과현황 막대 차트 그리기
|
||||||
|
drawBarChart(perfChartId, stat.performanceData);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawPieChart(id, dataArr) {
|
||||||
|
var canvas = document.getElementById(id);
|
||||||
|
if(!canvas) return;
|
||||||
|
|
||||||
|
new Chart(canvas, {
|
||||||
|
type: 'pie',
|
||||||
data: {
|
data: {
|
||||||
labels: labels,
|
labels: ['미입력', '입력중', '검수대기', '검수중', '수정요청', '검수완료', '등록완료'],
|
||||||
datasets: [{
|
datasets: [{
|
||||||
label: '등록 건수',
|
data: dataArr,
|
||||||
data: data,
|
backgroundColor: [
|
||||||
backgroundColor: 'rgba(75, 192, 192, 0.5)', // 색상 설정
|
'#e0e0e0', // 미입력 (회색)
|
||||||
borderColor: 'rgba(75, 192, 192, 1)',
|
'#ffcd56', // 입력중 (노랑)
|
||||||
|
'#36a2eb', // 검수대기 (파랑)
|
||||||
|
'#ff6384', // 검수중 (빨강)
|
||||||
|
'#ff9f40', // 수정요청 (주황)
|
||||||
|
'#4bc0c0', // 검수완료 (청록)
|
||||||
|
'#9966ff' // 등록완료 (보라)
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
plugins: {
|
||||||
|
legend: { position: 'right' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawBarChart(id, dataArr) {
|
||||||
|
var canvas = document.getElementById(id);
|
||||||
|
if(!canvas) return;
|
||||||
|
|
||||||
|
new Chart(canvas, {
|
||||||
|
type: 'bar',
|
||||||
|
data: {
|
||||||
|
labels: ['타당성조사', '기본설계', '실시설계', '시공중', '준공', '유지보수'],
|
||||||
|
datasets: [{
|
||||||
|
label: '건수',
|
||||||
|
data: dataArr,
|
||||||
|
backgroundColor: '#36a2eb',
|
||||||
borderWidth: 1
|
borderWidth: 1
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
|
|
@ -187,18 +235,51 @@
|
||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
scales: {
|
scales: {
|
||||||
y: {
|
y: { beginAtZero: true, ticks: { stepSize: 1 } }
|
||||||
beginAtZero: true,
|
|
||||||
ticks: { stepSize: 1 } // 정수 단위 표시
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
plugins: {
|
plugins: {
|
||||||
legend: { display: false }, // 범례 숨김 (단일 데이터셋이므로)
|
legend: { display: false }
|
||||||
title: {
|
}
|
||||||
display: true,
|
}
|
||||||
text: '기관별 건설현장 등록 현황',
|
});
|
||||||
font: { size: 16 }
|
}
|
||||||
}
|
|
||||||
|
// [하단] 전체 기관별 등록 건수 차트
|
||||||
|
function updateInstitutionChart(instStats) {
|
||||||
|
var canvas = document.getElementById('institutionChart');
|
||||||
|
if(!canvas) return;
|
||||||
|
|
||||||
|
if (instChartInstance) instChartInstance.destroy();
|
||||||
|
|
||||||
|
var labels = [];
|
||||||
|
var data = [];
|
||||||
|
|
||||||
|
instStats.forEach(function(stat) {
|
||||||
|
var name = stat.name;
|
||||||
|
if (name === '울산광역시') name = '한국도로공사';
|
||||||
|
labels.push(name);
|
||||||
|
data.push(stat.count);
|
||||||
|
});
|
||||||
|
|
||||||
|
instChartInstance = new Chart(canvas, {
|
||||||
|
type: 'bar',
|
||||||
|
data: {
|
||||||
|
labels: labels,
|
||||||
|
datasets: [{
|
||||||
|
label: '등록 건수',
|
||||||
|
data: data,
|
||||||
|
backgroundColor: 'rgba(75, 192, 192, 0.5)',
|
||||||
|
borderColor: 'rgba(75, 192, 192, 1)',
|
||||||
|
borderWidth: 1
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
scales: { y: { beginAtZero: true, ticks: { stepSize: 1 } } },
|
||||||
|
plugins: {
|
||||||
|
legend: { display: false },
|
||||||
|
title: { display: true, text: '전체 기관별 등록 현황' }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -211,48 +292,24 @@
|
||||||
<div class="home-trainning">
|
<div class="home-trainning">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="row content">
|
<div class="row content">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
|
|
||||||
<!-- [신규] 기관별 통계 그래프 영역 추가 (하단 전체 너비) -->
|
|
||||||
|
<!-- 전체 기관별 등록 건수 종합 그래프 -->
|
||||||
<div class="row" style="margin-top: 30px;">
|
<div class="row" style="margin-top: 30px;">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<div class="well" style="background-color: #fff;">
|
<div class="well" style="background-color: #fff;">
|
||||||
<p class="fw-bold" style="font-size: 18px; color:#c87202; margin-bottom: 15px;">기관별 등록 건수</p>
|
<p class="fw-bold" style="font-size: 18px; color:#c87202; margin-bottom: 15px;">전체 기관별 등록 건수</p>
|
||||||
<div style="height: 400px;"> <!-- 높이 지정 -->
|
<div style="height: 400px;">
|
||||||
<canvas id="institutionChart"></canvas>
|
<canvas id="institutionChart"></canvas>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<!-- 기관별 상세 통계 그래프 영역 (동적 생성) -->
|
||||||
<!-- 최근 입력된 건설현장 -->
|
<div id="institution-detail-area" style="margin-top: 30px;">
|
||||||
<div class="col-sm-4">
|
<!-- 여기에 Javascript로 기관별 그래프들이 추가됨 -->
|
||||||
<div class="well" id="recent-project-area">
|
|
||||||
<p class="fw-bold" style="font-size: 18px; color:#c87202;">최근 입력된 건설현장</p>
|
|
||||||
<p>로딩 중...</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 단계별 건수 텍스트 -->
|
|
||||||
<div class="col-sm-4">
|
|
||||||
<div class="well">
|
|
||||||
<p class="fw-bold" style="font-size: 18px; color:#c87202;">단계별 건수</p>
|
|
||||||
<p><b>타당성조사 및 계획검토:</b> <span id="stage-feasibility">0건</span></p>
|
|
||||||
<p><b>기본설계:</b> 0건</p>
|
|
||||||
<p><b>실시설계:</b> 0건</p>
|
|
||||||
<p><b>시공중:</b> 0건</p>
|
|
||||||
<p><b>준공:</b> 0건</p>
|
|
||||||
<p><b>유지보수:</b> 0건</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 단계별 건수 그래프 -->
|
|
||||||
<div class="col-sm-4">
|
|
||||||
<div class="well">
|
|
||||||
<canvas id="myChart"></canvas>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue