통계 관리 수정

main
이범준 1 year ago
parent 378587ab00
commit db1e071c07

@ -5,7 +5,10 @@ import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Comparator; import java.util.Comparator;
import java.util.Date;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -171,4 +174,48 @@ public class CmmnUtil {
} }
} }
/**
* () .
* @param dayStr
* @return
*/
public static int compareToday(String dayStr) {
SimpleDateFormat yyyyMMddFormat = new SimpleDateFormat("yyyyMMdd");
Date curDate = new Date();
String strCurrentDate = yyyyMMddFormat.format(curDate);
if(dayStr.length() == 8) {
return Integer.parseInt(strCurrentDate) - Integer.parseInt(dayStr);
} else if(dayStr.length() == 14) {
return Integer.parseInt(strCurrentDate) - Integer.parseInt(dayStr.substring(0,8));
} else {
throw new RuntimeException("날짜 형식 오류");
}
}
/**
* .
* @param yyyyMMdd , add
* @return
*/
public static String addDay(String yyyyMMdd, int add) {
SimpleDateFormat yyyyMMddFormat = new SimpleDateFormat("yyyyMMdd");
Calendar cal = Calendar.getInstance();
try {
Date dt = yyyyMMddFormat.parse(yyyyMMdd);
cal.setTime(dt);
} catch (Exception e){
throw new RuntimeException(e);
}
cal.add(Calendar.DATE, add);
return yyyyMMddFormat.format(cal.getTime());
}
} }

@ -13,11 +13,11 @@ public class CodeConverter {
Map<String, List<CommonCode>> commonCodes = new HashMap<String, List<CommonCode>>(); Map<String, List<CommonCode>> commonCodes = new HashMap<String, List<CommonCode>>();
CodeConverter(Map<String, List<CommonCode>> commonCodes){ public CodeConverter(Map<String, List<CommonCode>> commonCodes){
this.commonCodes = commonCodes; this.commonCodes = commonCodes;
} }
String valueToCode(String codeGroupName, String value){ public String valueToCode(String codeGroupName, String value){
String result = ""; String result = "";
List<CommonCode> commonCodeList = commonCodes.get(codeGroupName); List<CommonCode> commonCodeList = commonCodes.get(codeGroupName);
@ -30,7 +30,7 @@ public class CodeConverter {
return result; return result;
} }
String codeToValue(String codeGroupName, String code){ public String codeToValue(String codeGroupName, String code){
String result = ""; String result = "";
List<CommonCode> commonCodeList = commonCodes.get(codeGroupName); List<CommonCode> commonCodeList = commonCodes.get(codeGroupName);

@ -24,27 +24,27 @@ public class Stat {
private String statSubTitle; private String statSubTitle;
/** /**
* *
*/ */
private String[] compositeItemNameTitle; private String aggregateYn;
/** /**
* *
*/ */
private String[] compositeValueTitle; private String[] itemsLabel;
/** /**
* *
*/ */
private String groupYn; private String[] numberValueLabel;
/** /**
* () * ()
*/ */
private List<StatItem> statItems; private List<StatItem> statItems;
/** /**
* () * ( ) ()
*/ */
private List<DataObject> resultList; private List<DataObject> resultList;

@ -10,28 +10,34 @@ import lombok.Setter;
@Setter @Setter
public class StatItem { public class StatItem {
/**
* ID
*/
private String itemId;
/** /**
* *
*/ */
private String itemName;; private String itemName;
/** /**
* *
*/ */
private String[] compositeItemName; private String[] compositeItemId;
/** /**
* *
*/ */
private String value; private String[] compositeItemName;
/** /**
* *
*/ */
private String[] compositeValue; private int[] numberValue;
/** /**
* *
*/ */
private List<DataObject> refList; private List<DataObject> refList;
} }

@ -16,5 +16,143 @@ public class StatQuery extends CmmnQuery {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/**
* (aggregate , list )
*/
private String structureType;
/**
* ()
*/
private String[] categorization;
/**
* (ID)
*/
private String[] categoryNameConverter;
/**
* ID ( )
*/
private String[] fixedItemId;
/**
* ( )
*/
private String[] regroupInfos;
/**
*
*/
private String namedNumberValueSeperator;
/**
*
*/
private String[] compositeNumberValueSeperator;
/**
* (count,sum)
*/
private String[] aggregateType;
/**
*
*/
private String dayColumn;
/**
*
*/
private int dayRange;
public String getStructureType() {
return ifEmpty(structureType, () -> null);
}
public <T extends StatQuery> T setStructureType(String structureType) {
this.structureType = structureType;
return self();
}
public String getNamedNumberValueSeperator() {
return ifEmpty(namedNumberValueSeperator, () -> null);
}
public <T extends StatQuery> T setNamedNumberValueSeperator(String namedNumberValueSeperator) {
this.namedNumberValueSeperator = namedNumberValueSeperator;
return self();
}
public String[] getCategorization() {
return ifEmpty(categorization, () -> null);
}
public <T extends StatQuery> T setCategorization(String... categorization) {
this.categorization = categorization;
return self();
}
public String[] getCategoryNameConverter() {
return ifEmpty(categoryNameConverter, () -> null);
}
public <T extends StatQuery> T setCategoryNameConverter(String... categoryNameConverter) {
this.categoryNameConverter = categoryNameConverter;
return self();
}
public String[] getRegroupInfos() {
return ifEmpty(regroupInfos, () -> null);
}
public <T extends StatQuery> T setRegroupInfos(String... regroupInfos) {
this.regroupInfos = regroupInfos;
return self();
}
public String[] getAggregateType() {
return ifEmpty(aggregateType, () -> null);
}
public <T extends StatQuery> T setAggregateType(String... aggregateType) {
this.aggregateType = aggregateType;
return self();
}
public String[] getCompositeNumberValueSeperator() {
return ifEmpty(compositeNumberValueSeperator, () -> null);
}
public <T extends StatQuery> T setCompositeNumberValueSeperator(String... compositeNumberValueSeperator) {
this.compositeNumberValueSeperator = compositeNumberValueSeperator;
return self();
}
public String[] getFixedItemId() {
return ifEmpty(fixedItemId, () -> null);
}
public <T extends StatQuery> T setFixedItemId(String... fixedItemId) {
this.fixedItemId = fixedItemId;
return self();
}
public int getDayRange() {
return ifEmpty(dayRange, () -> null);
}
public <T extends StatQuery> T setDayRange(int dayRange) {
this.dayRange = dayRange;
return self();
}
public String getDayColumn() {
return ifEmpty(dayColumn, () -> null);
}
public <T extends StatQuery> T setDayColumn(String dayColumn) {
this.dayColumn = dayColumn;
return self();
}
} }

@ -1,10 +1,14 @@
package cokr.xit.fims.stat.service; package cokr.xit.fims.stat.service;
import java.util.List;
import java.util.Map;
import cokr.xit.base.code.CommonCode;
import cokr.xit.fims.stat.Stat; import cokr.xit.fims.stat.Stat;
import cokr.xit.fims.stat.StatQuery; import cokr.xit.fims.stat.StatQuery;
public interface StatService { public interface StatService {
Stat getStatistics(StatQuery query); Stat getStatistics(StatQuery statQuery, Map<String, List<CommonCode>> commonCodes);
} }

@ -0,0 +1,320 @@
package cokr.xit.fims.stat.service.bean;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.stereotype.Component;
import cokr.xit.base.code.CommonCode;
import cokr.xit.fims.cmmn.CmmnQuery;
import cokr.xit.fims.crdn.parsing.CodeConverter;
import cokr.xit.fims.stat.Stat;
import cokr.xit.fims.stat.StatItem;
import cokr.xit.fims.stat.StatQuery;
import cokr.xit.foundation.AbstractComponent;
import cokr.xit.foundation.data.DataObject;
@Component("statBean")
public class StatBean extends AbstractComponent {
/** .
* @param queryResult SQL , statQuery
* @return
*/
public Stat groupByCartegory(List<DataObject> queryResult, StatQuery statQuery) {
Stat stat = new Stat();
//통계 요청 설정
int compositeSize = 0;
String[] compositeNumberValueSeperator = statQuery.getCompositeNumberValueSeperator();
String[] aggregateType = statQuery.getAggregateType();
if(compositeNumberValueSeperator == null && aggregateType == null) {
compositeSize = 1;
compositeNumberValueSeperator = new String[] {""};
aggregateType = new String[] {"count"};
} else if(compositeNumberValueSeperator != null && aggregateType == null) {
compositeSize = compositeNumberValueSeperator.length;
aggregateType = new String[compositeSize];
} else if(compositeNumberValueSeperator == null && aggregateType != null) {
compositeSize = aggregateType.length;
compositeNumberValueSeperator = new String[compositeSize];
} else {
if(compositeNumberValueSeperator.length > aggregateType.length) {
compositeSize = compositeNumberValueSeperator.length;
} else {
compositeSize = aggregateType.length;
}
}
for(int i=0; i < compositeSize; i++) {
if(compositeNumberValueSeperator[i] == null) {
compositeNumberValueSeperator[i] = "";
}
if(aggregateType[i] == null || aggregateType[i].equals("")) {
aggregateType[i] = "count";
}
}
statQuery.setAggregateType(aggregateType);
statQuery.setCompositeNumberValueSeperator(compositeNumberValueSeperator);
//통계 항목별 그룹핑
Map<String, List<DataObject>> group;
if(statQuery.getCategorization().length < 2) {
group = queryResult.stream()
.collect(Collectors.groupingBy(item -> item.string(statQuery.getCategorization()[0])));
} else {
String[] categorizations = statQuery.getCategorization();
group = queryResult.stream()
.collect(
Collectors.groupingBy(
(item) -> {
String result = "";
for(int i = 0; i < categorizations.length; i++) {
if(i != 0) {
result += ",";
}
result += item.string(categorizations[i]);
}
return result;
}
)
);
}
//고정 항목 설정
if(statQuery.getFixedItemId() != null) {
this.itemFix(group, statQuery.getFixedItemId());
}
//수치 값 추출
Set<String> keySet = group.keySet();
Iterator<String> it = keySet.iterator();
List<StatItem> statItems = new ArrayList<>();
while(it.hasNext()){
String key = it.next();
StatItem statItem = new StatItem();
if(statQuery.getCategorization().length < 2) {
statItem.setItemId(key);
} else {
statItem.setCompositeItemId(key.split(","));
}
List<DataObject> listByKey = group.get(key);
int[] numberValues = this.extractNumberValue(listByKey, statQuery);
statItem.setNumberValue(numberValues);
statItems.add(statItem);
}
stat.setStatItems(statItems);
return stat;
}
/** .
* @param group , fixedItemId ID;
* @return
*/
public void itemFix(Map<String, List<DataObject>> group, String[] fixedItemId) {
for(int i=0; i < fixedItemId.length; i++) {
if(!group.containsKey(fixedItemId[i])) {
group.put(fixedItemId[i], null);
}
}
Set<String> keySet = group.keySet();
Iterator<String> it = keySet.iterator();
List<String> deleteTargets = new ArrayList<>();
while(it.hasNext()) {
String key = it.next();
if(!Arrays.asList(fixedItemId).contains(key)) {
deleteTargets.add(key);
}
}
for(String deleteTarget : deleteTargets) {
group.remove(deleteTarget);
}
}
/** (, ) .
* @param listByKey , statQuery
* @return
*/
public int[] extractNumberValue(List<DataObject> listByKey, StatQuery statQuery) {
String[] compositeNumberValueSeperator = statQuery.getCompositeNumberValueSeperator();
String[] aggregateType = statQuery.getAggregateType();
int[] aggregateArr = new int[compositeNumberValueSeperator.length];
for(int i=0; i < compositeNumberValueSeperator.length; i++) {
aggregateArr[i] = 0;
}
if(listByKey != null) {
for(int i=0; i<listByKey.size(); i++) {
DataObject dataObject = listByKey.get(i);
for(int j=0; j < aggregateArr.length; j++) {
String condition = compositeNumberValueSeperator[j];
if(condition.contains("=")) {
String conditionColumn = condition.split("=")[0];
String conditionColumnValue = dataObject.string(conditionColumn);
String conditionValue = condition.split("=")[1];
if(conditionColumnValue.equals("")) {
continue;
}
if(conditionColumn.endsWith("_DT") && conditionValue.length() == 8) {
conditionColumnValue = conditionColumnValue.substring(0,8);
}
if(!conditionColumnValue.equals(conditionValue)) {
continue;
}
}
if(aggregateType[j].startsWith("sum=")) {
aggregateArr[j] += dataObject.number(aggregateType[j].split("=")[1]).intValue();
} else {
aggregateArr[j] += 1;
}
}
}
}
return aggregateArr;
}
/** .
* @param statItems , categoryNameConverter , allCode
* @return
*/
public void attachItemName(List<StatItem> statItems, String[] categoryNameConverter, Map<String, List<CommonCode>> allCode) {
CodeConverter codeConverter = new CodeConverter(allCode);
if(categoryNameConverter.length > 1) {
String[] codeGroups = categoryNameConverter;
for(StatItem statItem : statItems) {
String[] compositeItemId = statItem.getCompositeItemId();
String[] compositeItemName = new String[compositeItemId.length];
for(int i=0; i < compositeItemId.length ;i++) {
if(codeGroups[i].equals("")) {
compositeItemName[i] = compositeItemId[i];
} else {
String itemName = codeConverter.codeToValue(codeGroups[i], compositeItemId[i]);
compositeItemName[i] = itemName;
}
}
statItem.setCompositeItemName(compositeItemName);
}
} else {
for(StatItem statItem : statItems) {
String itemId = statItem.getItemId();
String itemName = codeConverter.codeToValue(categoryNameConverter[0] , itemId);
statItem.setItemName(itemName);
}
}
}
/** .
* @param statItems , regroupInfos
* @return
*/
public void regroupItem(List<StatItem> statItems, String[] regroupInfos) {
for(int i=0; i < regroupInfos.length; i++) {
String[] regroupInfo = regroupInfos[i].split("=");
String[] sourceCodes = regroupInfo[0].split(",");
String targetCodeName = regroupInfo[1];
StatItem newItem = new StatItem();
newItem.setItemId("regroup"+i);
newItem.setItemName(targetCodeName);
int[] numberValues = null;
for(StatItem statItem : statItems) {
if(Arrays.asList(sourceCodes).contains(statItem.getItemId())) {
if(numberValues == null) {
numberValues = statItem.getNumberValue();
} else {
for(int j=0; j < numberValues.length; j++) {
numberValues[j] += statItem.getNumberValue()[j];
}
}
}
}
newItem.setNumberValue(numberValues);
statItems.removeIf(item -> Arrays.asList(sourceCodes).contains(item.getItemId()));
statItems.add(newItem);
}
}
/** .
* @param sql query, dayCol , from , to
* @return
*/
public void daySetting(CmmnQuery someQuery, String dayCol, String from, String to) {
try {
if(dayCol.equals("REG_DT")) {
Method m0 = CmmnQuery.class.getDeclaredMethod("setSchDateOpt", String.class);
m0.invoke(someQuery, "regDt");
Method m1 = CmmnQuery.class.getDeclaredMethod("setSchDateFrom", String.class);
m1.invoke(someQuery, from);
Method m2 = CmmnQuery.class.getDeclaredMethod("setSchDateTo", String.class);
m2.invoke(someQuery, to);
} else {
if(dayCol.equals("CRDN_YMD")) {
Method m1 = someQuery.getClass().getDeclaredMethod("setSchCrdnYmdFrom", String.class);
m1.invoke(someQuery, from);
Method m2 = someQuery.getClass().getDeclaredMethod("setSchCrdnYmdTo", String.class);
m2.invoke(someQuery, to);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

@ -1,16 +1,28 @@
package cokr.xit.fims.stat.service.bean; package cokr.xit.fims.stat.service.bean;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource; import javax.annotation.Resource;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import cokr.xit.base.code.CommonCode;
import cokr.xit.fims.cmmn.CmmnUtil;
import cokr.xit.fims.crdn.CrdnQuery;
import cokr.xit.fims.crdn.service.CrdnService; import cokr.xit.fims.crdn.service.CrdnService;
import cokr.xit.fims.excl.LevyExclQuery;
import cokr.xit.fims.excl.service.OpnnSbmsnService; import cokr.xit.fims.excl.service.OpnnSbmsnService;
import cokr.xit.fims.sndb.SndbQuery;
import cokr.xit.fims.sndb.service.SndngService; import cokr.xit.fims.sndb.service.SndngService;
import cokr.xit.fims.stat.Stat; import cokr.xit.fims.stat.Stat;
import cokr.xit.fims.stat.StatQuery; import cokr.xit.fims.stat.StatQuery;
import cokr.xit.fims.stat.service.StatService; import cokr.xit.fims.stat.service.StatService;
import cokr.xit.foundation.component.AbstractServiceBean; import cokr.xit.foundation.component.AbstractServiceBean;
import cokr.xit.foundation.data.DataObject;
@Service("statService") @Service("statService")
public class StatServiceBean extends AbstractServiceBean implements StatService { public class StatServiceBean extends AbstractServiceBean implements StatService {
@ -24,13 +36,175 @@ public class StatServiceBean extends AbstractServiceBean implements StatService
@Resource(name = "sndngService") @Resource(name = "sndngService")
private SndngService sndngService; private SndngService sndngService;
@Resource(name = "statBean")
private StatBean statBean;
@Override @Override
public Stat getStatistics(StatQuery query) { public Stat getStatistics(StatQuery statQuery, Map<String, List<CommonCode>> commonCodes) {
Stat stat = new Stat(); Stat stat = new Stat();
List<DataObject> queryResult = new ArrayList<>();
SimpleDateFormat yyyyMMdd = new SimpleDateFormat("yyyyMMdd");
Date curDate = new Date();
String today = yyyyMMdd.format(curDate);
boolean daySetting = false;
if(statQuery.getStructureType().equals("aggregate")) {
String[] numberValueLabel = null;
int dayRange = statQuery.getDayRange() == 0 ? 15 : statQuery.getDayRange();
String firstDay = CmmnUtil.addDay(today, (-1 * (dayRange-1)));
String dayColumn = statQuery.getDayColumn() == null ? "REG_DT" : statQuery.getDayColumn();
if(statQuery.getNamedNumberValueSeperator().equals("completeAndTotal")) { //완료자료 및 전체자료
String[] compositeNumberValueSeperator = new String[] {"COMPLETE_YN=Y", ""};
statQuery.setCompositeNumberValueSeperator(compositeNumberValueSeperator);
if(statQuery.getAggregateType() == null) {
String[] aggregateType = new String[] {"count", "count"};
statQuery.setAggregateType(aggregateType);
}
daySetting = true;
numberValueLabel = new String[] {"처리건수","전체건수"};
} else if(statQuery.getNamedNumberValueSeperator().equals("lastFewDays")) {
String[] compositeNumberValueSeperator = new String[dayRange];
for(int i=0; i<compositeNumberValueSeperator.length; i++) {
compositeNumberValueSeperator[i] = dayColumn+"="+CmmnUtil.addDay(firstDay,i);
}
statQuery.setCompositeNumberValueSeperator(compositeNumberValueSeperator);
daySetting = true;
if(statQuery.getAggregateType() == null) {
String[] aggregateType = new String[compositeNumberValueSeperator.length];
for(int i=0; i<compositeNumberValueSeperator.length; i++) {
aggregateType[i] = "count";
}
statQuery.setAggregateType(aggregateType);
} else if(statQuery.getAggregateType().length != statQuery.getCompositeNumberValueSeperator().length) {
String[] aggregateType = new String[compositeNumberValueSeperator.length];
String defaultType = statQuery.getAggregateType()[0];
if(ifEmpty(defaultType,()->"").equals("")) {
defaultType = "count";
}
for(int i=0; i<compositeNumberValueSeperator.length; i++) {
aggregateType[i] = defaultType;
}
statQuery.setAggregateType(aggregateType);
}
numberValueLabel = new String[dayRange];
for(int i=0; i < dayRange; i++) {
if(i == (dayRange-1)) {
numberValueLabel[i] = "금일";
} else {
numberValueLabel[i] = Integer.toString((dayRange - (i+1)));
}
}
}
String domain = "";
String[] categorizations = statQuery.getCategorization();
for(String categorization : categorizations) {
switch(categorization) {
case "CRDN_SE_CD" :
case "등록대상,이첩대상" :
case "초기자료처리" :
domain = "crdn";
break;
case "SNDNG_SE_CD" :
domain = "sndb";
break;
case "OPNN_SBMSN_STTS_CD" :
domain = "excl";
break;
case "수납상태" :
domain = "levy";
break;
}
if(!domain.equals("")) {
break;
}
}
if(domain.equals("")) {
throw new RuntimeException("통계 유형 확인 오류");
}
//
if(domain.equals("crdn")) {
CrdnQuery crdnQuery = new CrdnQuery();
crdnQuery.setSggCd(statQuery.getSggCd());
if(daySetting) {
statBean.daySetting(crdnQuery, dayColumn, firstDay, today);
}
queryResult = crdnService.getCrackdownList(crdnQuery);
}
else if(domain.equals("sndb")) {
SndbQuery sndbQuery = new SndbQuery();
if(daySetting) {
statBean.daySetting(sndbQuery, dayColumn, firstDay, today);
}
//queryResult = sndngService.getList(sndbQuery)
}
else if(domain.equals("excl")) {
LevyExclQuery levyExclQuery = new LevyExclQuery();
if(daySetting) {
statBean.daySetting(levyExclQuery, dayColumn, firstDay, today);
}
//queryResult = opnnSbmsnService.getList(levyExclQuery)
}
else if(domain.equals("levy")) {
}
stat = statBean.groupByCartegory(queryResult, statQuery);
statBean.attachItemName(stat.getStatItems(), statQuery.getCategoryNameConverter(), commonCodes);
if(statQuery.getRegroupInfos() != null){
statBean.regroupItem(stat.getStatItems(), statQuery.getRegroupInfos());
}
if(numberValueLabel != null) {
stat.setNumberValueLabel(numberValueLabel);
}
stat.setAggregateYn("Y");
}
return stat; return stat;

@ -1,10 +1,17 @@
package cokr.xit.fims.stat.web; package cokr.xit.fims.stat.web;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource; import javax.annotation.Resource;
import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.ModelAndView;
import cokr.xit.base.code.CommonCode;
import cokr.xit.base.user.ManagedUser;
import cokr.xit.base.user.dao.UserMapper;
import cokr.xit.base.web.ApplicationController; import cokr.xit.base.web.ApplicationController;
import cokr.xit.fims.crdn.dao.GlobalStngMapper;
import cokr.xit.fims.stat.Stat; import cokr.xit.fims.stat.Stat;
import cokr.xit.fims.stat.StatQuery; import cokr.xit.fims.stat.StatQuery;
import cokr.xit.fims.stat.service.StatService; import cokr.xit.fims.stat.service.StatService;
@ -18,10 +25,25 @@ public class StatController extends ApplicationController {
@Resource(name="statService") @Resource(name="statService")
private StatService statService; private StatService statService;
public ModelAndView getStatistics(StatQuery query) { @Resource(name="globalStngMapper")
protected GlobalStngMapper globalStngMapper;
@Resource(name="userMapper")
protected UserMapper userMapper;
public ModelAndView getStatistics(StatQuery statQuery) {
ModelAndView mav = new ModelAndView("jsonView"); ModelAndView mav = new ModelAndView("jsonView");
Stat stat = statService.getStatistics(query); ManagedUser currentUser = userMapper.getUser(currentUser().getAccount(), currentUser().getInstitute());
String curDeptCode = currentUser.getDeptCode();
String sggCd = globalStngMapper.selectSggCd(curDeptCode);
statQuery.setSggCd(sggCd);
String[] categoryNameConverter = statQuery.getCategoryNameConverter();
Map<String, List<CommonCode>> commonCodes = getCodesOf(categoryNameConverter);
Stat stat = statService.getStatistics(statQuery, commonCodes);
mav.addObject("stat", stat); mav.addObject("stat", stat);
return mav; return mav;

@ -7,6 +7,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.ModelAndView;
import cokr.xit.fims.sprt.SprtQuery; import cokr.xit.fims.sprt.SprtQuery;
import cokr.xit.fims.stat.StatQuery;
@Controller @Controller
public class CmnController { public class CmnController {
@ -364,6 +365,18 @@ public class CmnController {
} }
@Controller
@RequestMapping(name="통계", value="/stat")
class StatController extends cokr.xit.fims.stat.web.StatController {
@Override
@RequestMapping(name="통계 요청", value="/010/info.do")
public ModelAndView getStatistics(StatQuery query) {
return super.getStatistics(query);
}
}
@Controller @Controller
@RequestMapping(name="납부자", value="/payer") @RequestMapping(name="납부자", value="/payer")
class PayerController extends cokr.xit.fims.payer.web.PayerController { class PayerController extends cokr.xit.fims.payer.web.PayerController {

@ -5,20 +5,28 @@
<div id="card1" class="card dashboard-total"> <div id="card1" class="card dashboard-total">
<div class="card-body row"> <div class="card-body row">
<div class="col px-4 card-separator d-flex flex-column align-items-center"> <div class="col px-4 card-separator d-flex flex-column align-items-center">
<p class="mb-1" id="card1-1">?/?</p> <p class="mb-1" id="card1-1">
<span class="skeleton">?/?</span>
</p>
<i class="svg-cctv-fixed w-px-30 d-block" title="고정형CCTV"></i> <i class="svg-cctv-fixed w-px-30 d-block" title="고정형CCTV"></i>
</div> </div>
<div class="col px-4 card-separator d-flex flex-column align-items-center"> <div class="col px-4 card-separator d-flex flex-column align-items-center">
<p class="mb-1" id="card1-2">?/?</p> <p class="mb-1" id="card1-2">
<i class="svg-crackdown-road w-px-30 d-block" title="도보단속"></i> <span class="skeleton">?/?</span>
</p>
<i class="svg-crackdown-road w-px-30 d-block" title="도보"></i>
</div> </div>
<div class="col px-4 card-separator d-flex flex-column align-items-center"> <div class="col px-4 card-separator d-flex flex-column align-items-center">
<p class="mb-1" id="card1-3">?/?</p> <p class="mb-1" id="card1-3">
<i class="svg-cctv-drive w-px-30 d-block" title="이동형CCTV"></i> <span class="skeleton">?/?</span>
</p>
<i class="svg-cctv-drive w-px-30 d-block" title="주행형CCTV"></i>
</div> </div>
<div class="col px-4 d-flex flex-column align-items-center"> <div class="col px-4 d-flex flex-column align-items-center">
<p class="mb-1" id="card1-4">?/?</p> <p class="mb-1" id="card1-4">
<i class="svg-crackdown-minwon w-px-30 d-block" title="민원(즉시단속)"></i> <span class="skeleton">?/?</span>
</p>
<i class="svg-crackdown-minwon w-px-30 d-block" title="민원"></i>
</div> </div>
</div> </div>
<div class="card-footer ms-auto"> <div class="card-footer ms-auto">
@ -29,11 +37,15 @@
<div id="card2" class="card dashboard-total"> <div id="card2" class="card dashboard-total">
<div class="card-body row"> <div class="card-body row">
<div class="col px-4 card-separator d-flex flex-column align-items-center"> <div class="col px-4 card-separator d-flex flex-column align-items-center">
<p class="mb-1" id="card2-1">?/?</p> <p class="mb-1" id="card2-1">
<span class="skeleton">?/?</span>
</p>
<i class="svg-target-lvy w-px-30 d-block" title="등록대상"></i> <i class="svg-target-lvy w-px-30 d-block" title="등록대상"></i>
</div> </div>
<div class="col px-4 d-flex flex-column align-items-center"> <div class="col px-4 d-flex flex-column align-items-center">
<p class="mb-1" id="card2-2">?/?</p> <p class="mb-1" id="card2-2">
<span class="skeleton">?/?</span>
</p>
<i class="svg-target-transfer w-px-30 d-block" title="이첩대상"></i> <i class="svg-target-transfer w-px-30 d-block" title="이첩대상"></i>
</div> </div>
</div> </div>
@ -45,15 +57,21 @@
<div id="card3" class="card dashboard-total"> <div id="card3" class="card dashboard-total">
<div class="card-body row"> <div class="card-body row">
<div class="col px-4 card-separator d-flex flex-column align-items-center"> <div class="col px-4 card-separator d-flex flex-column align-items-center">
<p class="mb-1" id="card3-1">?/?</p> <p class="mb-1" id="card3-1">
<span class="skeleton">?/?</span>
</p>
<i class="svg-sendstat-guide w-px-30 d-block" title="계도장 발송현황"></i> <i class="svg-sendstat-guide w-px-30 d-block" title="계도장 발송현황"></i>
</div> </div>
<div class="col px-4 card-separator d-flex flex-column align-items-center"> <div class="col px-4 card-separator d-flex flex-column align-items-center">
<p class="mb-1" id="card3-2">?/?</p> <p class="mb-1" id="card3-2">
<span class="skeleton">?/?</span>
</p>
<i class="svg-sendstat-before w-px-30 d-block" title="사전통보 발송현황"></i> <i class="svg-sendstat-before w-px-30 d-block" title="사전통보 발송현황"></i>
</div> </div>
<div class="col px-4 d-flex flex-column align-items-center"> <div class="col px-4 d-flex flex-column align-items-center">
<p class="mb-1" id="card3-3">?/?</p> <p class="mb-1" id="card3-3">
<span class="skeleton">?/?</span>
</p>
<i class="svg-sendstat-nop w-px-30 d-block" title="고지서 발송현황"></i> <i class="svg-sendstat-nop w-px-30 d-block" title="고지서 발송현황"></i>
</div> </div>
</div> </div>
@ -65,15 +83,21 @@
<div id="card4" class="card dashboard-total"> <div id="card4" class="card dashboard-total">
<div class="card-body row"> <div class="card-body row">
<div class="col px-4 card-separator d-flex flex-column align-items-center"> <div class="col px-4 card-separator d-flex flex-column align-items-center">
<p class="mb-1" id="card4-1">?/?</p> <p class="mb-1" id="card4-1">
<span class="skeleton">?/?</span>
</p>
<i class="svg-opn-rcp w-px-30 d-block" title="접수"></i> <i class="svg-opn-rcp w-px-30 d-block" title="접수"></i>
</div> </div>
<div class="col px-4 card-separator d-flex flex-column align-items-center"> <div class="col px-4 card-separator d-flex flex-column align-items-center">
<p class="mb-1" id="card4-2">?/?</p> <p class="mb-1" id="card4-2">
<span class="skeleton">?/?</span>
</p>
<i class="svg-opn-decision w-px-30 d-block" title="수용/미수용"></i> <i class="svg-opn-decision w-px-30 d-block" title="수용/미수용"></i>
</div> </div>
<div class="col px-4 d-flex flex-column align-items-center"> <div class="col px-4 d-flex flex-column align-items-center">
<p class="mb-1" id="card4-3">?/?</p> <p class="mb-1" id="card4-3">
<span class="skeleton">?/?</span>
</p>
<i class="svg-opn-selfdrop w-px-30 d-block" title="자진취하"></i> <i class="svg-opn-selfdrop w-px-30 d-block" title="자진취하"></i>
</div> </div>
</div> </div>
@ -151,7 +175,7 @@ height="250" viewBox="0 0 24 24" style="fill: rgba(0, 0, 0, 1);transform: ;msFil
</svg>`; </svg>`;
fnMakeSkeleton(); fnMakeSkeleton();
sleep(3000).then(() => fnLoadStatisticsData()); sleep(3000).then(() => fnLoadStatisticsDatas());
//데이터 로딩 전 이미지 표시 //데이터 로딩 전 이미지 표시
@ -168,85 +192,105 @@ function fnMakeSkeleton(){
$("#doughnutChart3CardBody").append(falseDoughnutChart); $("#doughnutChart3CardBody").append(falseDoughnutChart);
} }
//통계 데이터 조회
function fnLoadStatisticsData(){
//TODO : ajax //통계 데이터 조회(여러 건)
function fnLoadStatisticsDatas(){
var data = {}; var queryParam = {};
data = { //단속구분별 전체/완료 건수
statItems : [ queryParam = {
{ itemName : "고정형CCTV", compositeValue : [2,10] }, structureType : "aggregate",
{ itemName : "도보단속", compositeValue : [2,3] }, categorization : ["CRDN_SE_CD"],
{ itemName : "이동형CCTV", compositeValue : [2,3] }, categoryNameConverter : ["FIM002"],
{ itemName : "민원(즉시단속)", compositeValue : [10,10] } fixedItemId : ["01","02","06","13","08","09","10","11"],
] regroupInfos : ["08,09,10,11=민원","06,13=도보"],
namedNumberValueSeperator : "completeAndTotal"
}; };
fnRenderDashboardContents(data, "card1" , "progressAndTotal"); fnLoadStatisticsData(queryParam, "card1", "progressAndTotal");
data = { //초기자료처리별 전체/완료건수
statItems : [ queryParam = {
{ itemName : "등록대상", compositeValue : [45,50] }, structureType : "aggregate",
{ itemName : "이첩대상", compositeValue : [35,40] } categorization : ["등록대상이첩대상구분"],
] categoryNameConverter : ["FIM999"],
fixedItemId : ["등록대상","이첩대상"],
namedNumberValueSeperator : "completeAndTotal"
}; };
fnRenderDashboardContents(data, "card2" , "progressAndTotal"); //fnLoadStatisticsData(queryParam, "card2", "progressAndTotal");
data = { //발송문서별 전체/완료 건수
statItems : [ queryParam = {
{ itemName : "계도장", compositeValue : [4,10] }, structureType : "aggregate",
{ itemName : "사전통보", compositeValue : [5,11] }, categorization : ["SNDNG_SE_CD"],
{ itemName : "고지서", compositeValue : [6,12] } categoryNameConverter : ["FIM047"],
] fixedItemId : ["01", "02", "03"],
namedNumberValueSeperator : "completeAndTotal"
}; };
fnRenderDashboardContents(data, "card3" , "progressAndTotal"); //fnLoadStatisticsData(queryParam, "card3" , "progressAndTotal");
data = { //의견진술자료상태
statItems : [ queryParam = {
{ itemName : "접수", compositeValue : [49,100] }, structureType : "aggregate",
{ itemName : "수용/미수용", compositeValue : [50,98] }, categorization : ["OPNN_SBMSN_STTS_CD"],
{ itemName : "자진취하", compositeValue : [51,97] } categoryNameConverter : ["FIM031"],
] fixedItemId : ["00", "01", "02", "03"],
regroupInfo : ["01,02=수용/미수용"],
namedNumberValueSeperator : "completeAndTotal"
}; };
fnRenderDashboardContents(data, "card4" , "progressAndTotal"); //fnLoadStatisticsData(queryParam, "card4" , "progressAndTotal");
data = { //단속구분별 최근n일 건수
compositeValueTitle : [14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, "금일"], queryParam = {
statItems : [ structureType : "aggregate",
{ itemName : "고정형", compositeValue : [80, 150, 180, 270, 210, 160, 160, 202, 265, 210, 270, 255, 290, 360, 375] }, categorization : ["CRDN_SE_CD"],
{ itemName : "도보", compositeValue : [80, 125, 105, 130, 215, 195, 140, 160, 230, 300, 220, 170, 210, 200, 280] }, categoryNameConverter : ["FIM002"],
{ itemName : "민원", compositeValue : [80, 99, 82, 90, 115, 115, 74, 75, 130, 155, 125, 90, 140, 130, 180] } fixedItemId : ["01","02","06","13","08","09","10","11"],
] regroupInfos : ["08,09,10,11=민원","06,13=도보"],
namedNumberValueSeperator : "lastFewDays",
dayRange : 15,
dayColumn : "CRDN_YMD"
}; };
fnRenderDashboardContents(data, "lineChart" , "line"); fnLoadStatisticsData(queryParam, "lineChart", "line");
data = { //초기자료처리별 건수 차트
statItems : [ queryParam = {
{ itemName : "단속", value : 15 }, structureType : "aggregate",
{ itemName : "계고", value : 15 }, categorization : ["초기자료처리"],
{ itemName : "서손", value : 70 } categoryNameConverter : ["FIM999"],
] fixedItemId : ["단속","계고","서손"]
}; };
fnRenderDashboardContents(data, "doughnutChart1", "doughnut"); //fnLoadStatisticsData(queryParam, "doughnutChart1", "doughnut");
data = { //발송문서종류별 건수 차트
statItems : [ queryParam = {
{ itemName : "계고장", value : 10 }, structureType : "aggregate",
{ itemName : "사전통보", value : 10 }, categorization : ["SNDNG_SE_CD"],
{ itemName : "고지서", value : 80 } categoryNameConverter : ["FIM047"],
] fixedItemId : ["01", "02", "03"]
}; };
fnRenderDashboardContents(data, "doughnutChart2", "doughnut"); //fnLoadStatisticsData(queryParam, "doughnutChart2", "doughnut");
data = { //의견진술결과별 건수 차트
statItems : [ queryParam = {
{ itemName : "수용", value : 33 }, structureType : "aggregate",
{ itemName : "미수용", value : 33 }, categorization : ["OPNN_SBMSN_STTS_CD"],
{ itemName : "자진취하", value : 34 } categoryNameConverter : ["FIM031"],
] fixedItemId : ["01", "02", "03"]
}; };
fnRenderDashboardContents(data, "doughnutChart3", "doughnut"); //fnLoadStatisticsData(queryParam, "doughnutChart3", "doughnut");
}
//통계 데이터 조회
function fnLoadStatisticsData(queryParam, cursor, statType){
ajax.get({
url : wctx.url("/stat/010/info.do"),
data : queryParam,
success : (resp) => {
var statData = resp.stat;
fnRenderDashboardContents(statData, cursor , statType);
}
});
} }
//대시보드 콘텐츠 표시 //대시보드 콘텐츠 표시
@ -273,10 +317,9 @@ function fnRenderDashboardContents(returnData, cursor, statType){
function fnRenderProgressAndTotal(returnData, cursor){ function fnRenderProgressAndTotal(returnData, cursor){
for(var i=0; i < returnData.statItems.length; i++){ for(var i=0; i < returnData.statItems.length; i++){
$("#"+cursor) var icon = $("#"+cursor).find("i[title='" + returnData.statItems[i].itemName + "']");
.find("p") var p = icon.prev("p");
.eq(i) p.html(returnData.statItems[i].numberValue[0] + "/" + returnData.statItems[i].numberValue[1]);
.html(returnData.statItems[i].compositeValue[0] + "/" + returnData.statItems[i].compositeValue[1]);
} }
if(cursor == "card1"){ if(cursor == "card1"){
@ -304,6 +347,9 @@ function fnRenderLine(returnData, cursor){
$("#"+cursor).show(); $("#"+cursor).show();
var maxOfStat = 0;
var minOfStat = 0;
var datasets = []; var datasets = [];
for(var i=0; i < returnData.statItems.length; i++){ for(var i=0; i < returnData.statItems.length; i++){
var defaultObject = { var defaultObject = {
@ -319,7 +365,17 @@ function fnRenderLine(returnData, cursor){
defaultObject.label = returnData.statItems[i].itemName; defaultObject.label = returnData.statItems[i].itemName;
defaultObject.data = returnData.statItems[i].compositeValue; defaultObject.data = returnData.statItems[i].numberValue;
var maxOfStatItem = Math.max(defaultObject.data);
var minOfStatItem = Math.min(defaultObject.data);
if(maxOfStatItem > maxOfStat){
maxOfStat = maxOfStatItem;
}
if(minOfStatItem < minOfStat){
minOfStat = minOfStatItem;
}
if(i % 3 == 0){ if(i % 3 == 0){
defaultObject.backgroundColor = config.colors.danger; defaultObject.backgroundColor = config.colors.danger;
@ -340,8 +396,9 @@ function fnRenderLine(returnData, cursor){
datasets.push(defaultObject); datasets.push(defaultObject);
} }
var xAxisLabels = returnData.compositeValueTitle; var xAxisLabels = returnData.numberValueLabel;
var yAxisConf = initYAxis(maxOfStat, minOfStat);
var lineChart = document.getElementById(cursor); var lineChart = document.getElementById(cursor);
if (lineChart) { if (lineChart) {
@ -352,6 +409,7 @@ function fnRenderLine(returnData, cursor){
datasets: datasets datasets: datasets
}, },
options: { options: {
lineTension : 0,
responsive: true, responsive: true,
maintainAspectRatio: false, maintainAspectRatio: false,
scales: { scales: {
@ -369,11 +427,11 @@ function fnRenderLine(returnData, cursor){
scaleLabel: { scaleLabel: {
display: true display: true
}, },
min: 0, min: yAxisConf.yAxisMin,
max: 400, max: yAxisConf.yAxisMax,
ticks: { ticks: {
color: "black", color: "black",
stepSize: 100 stepSize: yAxisConf.yAxisStep
}, },
grid: { grid: {
color: borderColor, color: borderColor,
@ -435,7 +493,7 @@ function fnRenderDoughnut(returnData, cursor){
var colors = []; var colors = [];
for(var i=0; i < returnData.statItems.length; i++){ for(var i=0; i < returnData.statItems.length; i++){
lebels.push(returnData.statItems[i].itemName); lebels.push(returnData.statItems[i].itemName);
datas.push(returnData.statItems[i].value); datas.push(returnData.statItems[i].numberValue[0]);
if(i % 3 == 0){ colors.push(cyanColor); } if(i % 3 == 0){ colors.push(cyanColor); }
if(i % 3 == 1){ colors.push(orangeLightColor); } if(i % 3 == 1){ colors.push(orangeLightColor); }
if(i % 3 == 2){ colors.push(config.colors.primary); } if(i % 3 == 2){ colors.push(config.colors.primary); }
@ -494,8 +552,46 @@ function fnRenderDoughnut(returnData, cursor){
} }
//차트y축 표시최대값,표시최소값,간격 설정
function initYAxis(max, min){
if(max <= 10){
var yAxisConf = {
yAxisMax : 10,
yAxisMin : 0,
yAxisStep : 2
};
return yAxisConf;
}
if(max <= 100){
var yAxisConf = {
yAxisMax : 100,
yAxisMin : 0,
yAxisStep : 20
};
return yAxisConf;
}
var yAxisMax = Math.ceil(max / 100) * 100
var yAxisMin = Math.floor(min / 100) * 100;
if(yAxisMax - yAxisMin <= 100){
yAxisMin = yAxisMax - 100;
}
var yAxisStep = (yAxisMax - yAxisMin)/5;
var yAxisConf = {
yAxisMax : yAxisMax,
yAxisMin : yAxisMin,
yAxisStep : yAxisStep
};
return yAxisConf;
}
</c:set> </c:set>

@ -39,7 +39,7 @@
보안모드 보안모드
</label> </label>
</div> </div>
<button type="button" class="btn btn-outline-dark"> <button type="button" id="btnGoToCvlcptDscsn" class="btn btn-outline-dark">
종합민원관리 종합민원관리
</button> </button>
<button type="button" class="btn btn-outline-dark"> <button type="button" class="btn btn-outline-dark">
@ -246,6 +246,9 @@ function doFastSearch(){
} }
} }
/*--------------------- 종합민원관리 클릭 이벤트 ---------------------*/
mappingButtonAndMenu("btnGoToCvlcptDscsn","민원상담");
/*--------------------- 사용자 메뉴얼 클릭 이벤트 ---------------------*/ /*--------------------- 사용자 메뉴얼 클릭 이벤트 ---------------------*/
$("#btnDownloadMenual--top").on( "click", function() { $("#btnDownloadMenual--top").on( "click", function() {

@ -87,16 +87,28 @@
max-width : 650px; max-width : 650px;
} }
@keyframes skeleton-loading { @keyframes skeleton-loading-fill {
0% { fill: #f2f2f2; } 0% { fill: #f2f2f2; }
25% { fill: #c1c1c1; } 25% { fill: #c1c1c1; }
50% { fill: #a1a1a1; } 50% { fill: #a1a1a1; }
75% { fill: #c1c1c1; } 75% { fill: #c1c1c1; }
100% { fill : #f2f2f2; } 100% { fill: #f2f2f2; }
} }
.skeleton { @keyframes skeleton-loading-color {
animation: skeleton-loading 1s infinite; 0% { color: #f2f2f2; }
25% { color: #c1c1c1; }
50% { color: #a1a1a1; }
75% { color: #c1c1c1; }
100% { color: #f2f2f2; }
}
svg.skeleton, path.skeleton {
animation: skeleton-loading-fill 1s infinite;
}
span.skeleton {
animation: skeleton-loading-color 1s infinite;
} }
.wrapper-list { .wrapper-list {

Loading…
Cancel
Save