package cokr.xit.fims.crdn.web; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import org.apache.poi.ss.usermodel.CellStyle; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.ModelAndView; import cokr.xit.base.code.CommonCode; import cokr.xit.base.docs.xls.CellDef; import cokr.xit.base.docs.xls.Comment; import cokr.xit.base.docs.xls.Format; import cokr.xit.base.docs.xls.Style; import cokr.xit.base.docs.xls.XLSWriter; import cokr.xit.base.file.FileInfo; import cokr.xit.base.file.FileInfo.Relation; import cokr.xit.base.file.service.FileQuery; import cokr.xit.base.file.service.FileService; import cokr.xit.base.file.service.bean.FileBean; import cokr.xit.base.file.web.FileInfoFactory; import cokr.xit.base.user.dao.UserMapper; import cokr.xit.base.web.ApplicationController; import cokr.xit.fims.cmmn.service.bean.StngBean; import cokr.xit.fims.cmmn.xls.FormatMaker; import cokr.xit.fims.cmmn.xls.StyleMaker; import cokr.xit.fims.crdn.Crdn; import cokr.xit.fims.crdn.CrdnQuery; import cokr.xit.fims.crdn.dao.CrdnInfoMapper; import cokr.xit.fims.crdn.dao.CrdnStngMapper; import cokr.xit.fims.crdn.service.CrdnService; import cokr.xit.fims.crdn.service.CrdnStngService; import cokr.xit.fims.mngt.service.bean.TaskProcessor; import cokr.xit.fims.sprt.Sprt; import cokr.xit.fims.sprt.service.bean.MediaBean; import cokr.xit.fims.task.Task; import cokr.xit.foundation.data.DataObject; /**단속 관리 서비스 웹 컨트롤러.
* {웹 컨텍스트}/crdn/crdn06 로 접근할 수 있다. * @author leebj */ @Controller @RequestMapping(name="단속 관리", value=Crdn06Controller.CLASS_URL) public class Crdn06Controller extends ApplicationController { public static final String CLASS_URL = "/crdn/crdn06"; public class METHOD_URL { public static final String crackdownManagementMain = "/010/main.do" , getCrackdownList = "/010/list.do" , removeCrackdown = "/010/remove.do" , removeCrackdownPayer = "/010/removeCrdnPayer.do" , countCrdn = "/010/nocs.do" , getCrackdownInfo = "/020/info.do" , updateCrackdown = "/020/update.do" , getCrackdownStatusInfo = "/030/info.do" , updateCrackdownStatus = "/030/update.do" ; } @Resource(name="crdnService") private CrdnService crdnService; @Resource(name="crdnStngService") private CrdnStngService crdnStngService; @Resource(name="userMapper") protected UserMapper userMapper; @Resource(name="crdnStngMapper") protected CrdnStngMapper crdnStngMapper; @Resource(name = "fileBean") private FileBean fileBean; @Resource(name = "stngBean") private StngBean stngBean; @Resource(name = "crdnInfoMapper") private CrdnInfoMapper crdnInfoMapper; @Resource(name = "mediaBean") private MediaBean mediaBean; @Resource(name = "fileService") private FileService fileService; /** 단속 관리 메인화면을 연다. * @return fims/crdn/crdn06010-main */ @RequestMapping(name="단속 관리 메인", value=METHOD_URL.crackdownManagementMain) public ModelAndView crackdownManagementMain(String openType, String taskSeCd) { Map> commonCodes = getCodesOf("FIM002", "FIM003", "FIM005", "FIM010", "FIM022", "FIM026", "FIM034", "FIM054"); ModelAndView mav = new ModelAndView("fims/crdn/crdn06010-main") .addObject("pageName", "crdn06010" + ifEmpty(openType, ()-> "")); if(!isEmpty(taskSeCd)) mav.addObject("taskSeCd", taskSeCd); mav.addObject("FIM003List", commonCodes.get("FIM003")) .addObject("FIM005List", commonCodes.get("FIM005")) .addObject("FIM010List", commonCodes.get("FIM010")) .addObject("FIM054List", commonCodes.get("FIM054")) .addObject("TaskListForSgg", stngBean.filterTaskSectionCodeForSgg(commonCodes.get("FIM054"))); return addCodes(commonCodes, mav, "FIM002", "FIM003", "FIM010", "FIM022", "FIM026", "FIM034"); } /**단속자료 목록을 조회한다.
* {@link CrdnService#getCrackdownList(CrdnQuery)} 참고 * @param query 단속자료 목록 조회 조건 * @return jsonView */ @Task @RequestMapping(name="단속자료 목록", value=METHOD_URL.getCrackdownList) public ModelAndView getCrackdownList(CrdnQuery query) { setFetchSize(query.setSggCd(currentUser().getOrgID())); if("xls".equals(query.getDownload())) { List cellDefs = fromJson(query.getCellDefs(), CellDef.listType()); if("Y".equals(query.getIncludePhoto())) { cellDefs.add((new CellDef()).setLabel("사진1").setWidth(18).setField("CRDN_PHOTO_PATH1")); cellDefs.add((new CellDef()).setLabel("사진2").setWidth(18).setField("CRDN_PHOTO_PATH2")); } XLSWriter xlsx = new XLSWriter().worksheet(0); Format format = new Format(xlsx); Comment comment = new Comment(xlsx); CellStyle center = format.cellStyle(Style.CENTER); CellStyle left = format.cellStyle(Style.LEFT); CellStyle dateYMD = format.yyyy_mm_dd(); CellStyle dateDT = format.yyyy_mm_dd_hh_mm_ss(); Map valueMap = new HashMap(); for(CellDef def : cellDefs) { switch(def.getLabel()) { case "자료출처"->{ valueMap.put("자료출처", format.of("CRDN_INPT_SE_NM").style(center)); } case "차량번호"->{ valueMap.put("차량번호", left); } case "위반일시"->{ valueMap.put("위반일시", FormatMaker.yyyy_mm_dd_hh_mm_ss(format,"CRDN_YMD_TM").style(dateDT)); } case "단속장소"->{ valueMap.put("단속장소", left); } case "사진건수"->{ valueMap.put("사진건수", center); } case "스티커번호"->{ valueMap.put("스티커번호", left); } case "장애차량확인"->{ valueMap.put("장애차량확인", format.of("PRK_PSBLTY_RSLT_NM").style(center)); } case "처리상태"->{ valueMap.put("처리상태", format.of("CRDN_STTS_NM").style(center)); } case "제외사유"->{ valueMap.put("제외사유", format.of("LEVY_EXCL_RSN_NM").style(center)); } case "제외처리일자"->{ valueMap.put("제외처리일자", FormatMaker.yyyy_mm_dd(format,"LEVY_EXCL_YMD").style(dateYMD)); } case "제외내역"->{ valueMap.put("제외내역", left); } case "사진1"->{ valueMap.put("사진1", FormatMaker.photoCellForApproval(format, comment, "CRDN_PHOTO_PATH1")); } case "사진2"->{ valueMap.put("사진2", FormatMaker.photoCellForApproval(format, comment, "CRDN_PHOTO_PATH2")); } } } CellDef.setValues(cellDefs, valueMap); List list = crdnService.getCrackdownList(query.setFetchSize(0)); if(ifEmpty(query.getIncludePhoto(), () -> "").equals("Y")) { for(DataObject crdn : list) { String crdnId = crdn.string("CRDN_ID"); FileQuery fileQuery = new FileQuery(); fileQuery.setInfoType(Crdn.INF_TYPE); fileQuery.setInfoKeys(crdnId); List fileInfoList = fileBean.getFileList(fileQuery); if(fileInfoList != null && fileInfoList.size() > 0) { for(int j=0; (j < fileInfoList.size()) && (j < 2); j++) { crdn.set("CRDN_PHOTO_PATH"+(j+1), fileInfoList.get(j).string("FILE_PATH")); } } } } xlsx.cell(0, 0).value("단속자료 목록").value(center).merge(0, cellDefs.size()-1); xlsx.cell(3, 0).rowValues(CellDef.header(cellDefs, () -> StyleMaker.headerStyle(xlsx))); xlsx.cell(4, 0).values(list, CellDef.values(cellDefs.stream().map(i -> {if(i.getValue() instanceof Format) {i.setField(null);}; return i;}).toList())); return new ModelAndView("downloadView") .addObject("download", xlsx.getDownloadable().setFilename("단속자료 목록.xlsx")) .addObject("downloadData", list) .addObject("dataNames", cellDefs.stream().map(CellDef::getLabel).toList()); } return setPagingInfo(new ModelAndView("jsonView"), crdnService.getCrackdownList(query),""); } @RequestMapping(name="차적조회조건", value="/010/vehicleReqs.do") public ModelAndView downloadVehicleReqs(String... crdnIds) { List list = crdnService.getCrackdownList(new CrdnQuery().setCrdnIds(crdnIds)).stream() .map(row -> { Object vhrno = row.remove("VHRNO"); Object crdnYmd = row.remove("CRDN_YMD"); row.clear(); row.set("VHRNO", vhrno) .set("CRDN_YMD", crdnYmd); return row; }) .toList(); XLSWriter xlsx = new XLSWriter().worksheet(0).cell(0, 0).values(list); return new ModelAndView("downloadView") .addObject("download", xlsx.getDownloadable().setFilename("차적조회조건-" + dateFormats.format("yyyyMMdd-HHmmss", System.currentTimeMillis()) + ".xlsx")); } @PostMapping(name="차적정보 업데이트", value="/010/uploadVehicles.do") public ModelAndView uploadVehicles(MultipartFile upload, String... crdnIds) { List list = crdnService.getCrackdownList(new CrdnQuery().setCrdnIds(crdnIds)).stream() .map(row -> { Object vhrno = row.remove("VHRNO"); Object crdnYmd = row.remove("CRDN_YMD"); row.clear(); row.set("VHRNO", vhrno) .set("CRDN_YMD", crdnYmd); return row; }) .toList(); XLSWriter xlsx = new XLSWriter().worksheet(0).cell(0, 0).values(list); return new ModelAndView("jsonView") .addObject("download", xlsx.getDownloadable().setFilename("차적조회조건-" + dateFormats.format("yyyyMMdd-HHmmss", System.currentTimeMillis()) + ".xlsx")); } /**지정한 단속 ID에 해당하는 단속 대장를 제거한다. * @param crdnID 단속 ID * @return jsonView *
 {
	 *     "affected": 저장된 정보수
	 *     "saved": 저장되었으면 true, 그렇지 않으면 false
	 * }
*/ @Task @RequestMapping(name="단속자료 삭제", value=METHOD_URL.removeCrackdown) public ModelAndView removeCrackdown(Crdn crdn) { String rtnMsg = crdnService.removeCrackdown(crdn); return new ModelAndView("jsonView") .addObject("saved", rtnMsg.contains("[S]")) .addObject("rtnMsg", rtnMsg); } /**지정한 단속 ID에 해당하는 납부자 ID를 제거한다. * @param crdnID 단속 ID * @return jsonView *
 {
	 *     "affected": 저장된 정보수
	 *     "saved": 저장되었으면 true, 그렇지 않으면 false
	 * }
*/ @Task @RequestMapping(name="단속자료 납부자 삭제", value=METHOD_URL.removeCrackdownPayer) public ModelAndView removeCrackdownPayer(Crdn crdn) { String rtnMsg = crdnService.removeCrackdownPayer(crdn); return new ModelAndView("jsonView") .addObject("saved", rtnMsg.contains("[S]")) .addObject("rtnMsg", rtnMsg); } /**지정한 조회조건으로 단속 건수를 반환한다. * @param CrdnQuery 조회조건 * @return jsonView */ @Task @RequestMapping(name="단속 건수 조회", value=METHOD_URL.countCrdn) public ModelAndView countCrdn(CrdnQuery crdnQuery) { int crdnCount = crdnService.countCrdn(crdnQuery); return new ModelAndView("jsonView") .addObject("crdnCount", crdnCount); } /**지정한 ID의 단속 정보(info)를 반환한다. * @param query 단속 대장 조회 조건 * @return fims/crdn/crdn06020-info 또는 jsonView *
{
	 *     "crdnInfo": 단속 정보
	 * }
*/ @Task @RequestMapping(name="단속자료 상세 조회", value=METHOD_URL.getCrackdownInfo) public ModelAndView getCrackdownInfo(HttpServletRequest hReq, CrdnQuery query) { // 법정동 List stdgNmList = crdnStngMapper.selectStdgNmList(currentUser().getOrgID()); boolean json = jsonResponse(); ModelAndView mav = new ModelAndView(json ? "jsonView" : "fims/crdn/crdn06020-info") .addObject("stdgNmList", stdgNmList); // 공통코드 - 위반 내역 코드 TaskProcessor taskProcessor = TaskProcessor.get(); String taskSeCd = query.getTaskSeCd(); String vltnByTask = taskProcessor.getVltnCdGrp(taskSeCd); Map> commonCodes = getCodesOf("CMN006", "FIM011","FIM053","LVS005",vltnByTask); mav.addObject("CMN006List", commonCodes.get("CMN006")) .addObject("FIM011List", commonCodes.get("FIM011")) .addObject("FIM053List", commonCodes.get("FIM053")) .addObject("LVS005List", commonCodes.get("LVS005")) .addObject("VLTNList", commonCodes.get(vltnByTask)); // 단속조 List teamList = crdnStngService.getTeamList(query); // 단속부가정보(TB_CRDN_ADI) 공통코드 String[] extraCodeGroups = taskProcessor.getExtraCdGrps(taskSeCd); if (!isEmpty(extraCodeGroups)) { commonCodes = getCodesOf(extraCodeGroups); commonCodes.forEach((codeGroup, codes) -> { mav.addObject(codeGroup + "List", codes); }); } mav.addObject("TeamList", teamList); DataObject crdnInfo = crdnService.getCrackdownInfo(query); return mav .addObject("pageName", "crdn06020") // jsp pageName .addObject("prefixUrl", CLASS_URL) // prefixUrl .addObject("Info", json ? crdnInfo : toJson(crdnInfo)) ; } /**지정한 단속 ID에 해당하는 단속 대장를 수정한다. * @param crdnID 단속 ID * @return jsonView *
 {
	 *     "affected": 저장된 정보수
	 *     "saved": 저장되었으면 true, 그렇지 않으면 false
	 * }
*/ @Task @RequestMapping(name="단속자료 수정", value=METHOD_URL.updateCrackdown) public ModelAndView updateCrackdown(Crdn crdn) { String rtnMsg = crdnService.updateCrackdown(crdn); return new ModelAndView("jsonView") .addObject("saved", rtnMsg.contains("[S]")) .addObject("rtnMsg", rtnMsg); } /**지정한 ID의 단속 처리 상태 정보(info)를 반환한다. * @param query 단속 대장 조회 조건 * @return fims/crdn/crdn06030-info 또는 jsonView *
{
	 *     "crdnInfo": 단속 정보
	 * }
*/ @Task @RequestMapping(name="단속자료 상태 조회", value=METHOD_URL.getCrackdownStatusInfo) public ModelAndView getCrackdownStatusInfo(HttpServletRequest hReq, CrdnQuery query) { DataObject crdnInfo = crdnService.getCrackdownInfo(query); Map> commonCodes = getCodesOf("FIM010"); boolean json = jsonResponse(); return new ModelAndView(json ? "jsonView" : "fims/crdn/crdn06030-info") .addObject("pageName", "crdn06030") // jsp pageName .addObject("prefixUrl", CLASS_URL) // prefixUrl .addObject("FIM010List", commonCodes.get("FIM010")) // 단속 처리 상태 코드(CRDN_STTS_CD) .addObject("Info", json ? crdnInfo : toJson(crdnInfo)) ; } /**지정한 단속 ID에 해당하는 단속 처리 상태를 수정한다. * @param crdnID 단속 ID * @return jsonView *
 {
	 *     "affected": 저장된 정보수
	 *     "saved": 저장되었으면 true, 그렇지 않으면 false
	 * }
*/ @Task @RequestMapping(name="단속처리상태 수정", value=METHOD_URL.updateCrackdownStatus) public ModelAndView updateCrackdownStatus(Crdn crdn) { String rtnMsg = crdnService.updateCrackdownStatus(crdn); return new ModelAndView("jsonView") .addObject("saved", rtnMsg.contains("[S]")) .addObject("rtnMsg", rtnMsg); } @PostMapping(name= "단속 사진 추가", value = "/insertCrdnImageFile.do") public ModelAndView insertCrdnImageFile(String crdnId, int atchFileCnt, MultipartFile[] newFileList) { String retMessage = "[F] "; if(newFileList == null || newFileList.length == 0 || newFileList[0] == null || newFileList[0].isEmpty()) throw new RuntimeException("파일 업로드 오류"); Relation relation = new Relation().setInfoType(Crdn.INF_TYPE).setInfoKey(crdnId); List newFileInfoList = new FileInfoFactory().makeFileInfos(relation, newFileList); List> processList = new ArrayList<>(); int newFileSort = 999; for(FileInfo newFileInfo : newFileInfoList) { newFileInfo.setSortOrder(newFileSort++); processList.add(Map.of("process", "insert", "obj", newFileInfo)); } Crdn crdn = new Crdn(); crdn.setCrdnId(crdnId); crdn.setAtchFileCnt(atchFileCnt); retMessage = crdnService.changeCrdnImageFile(crdn, processList); return new ModelAndView("jsonView") .addObject("saved", retMessage.contains("[S]")) .addObject("retMessage", retMessage); } @PostMapping(name= "단속 사진 수정", value = "/updateCrdnImageFile.do") public ModelAndView updateCrdnImageFile(String crdnId, int atchFileCnt, String beforeKey, int beforeOrder, MultipartFile modifyFile) { if(isEmpty(modifyFile)) throw new RuntimeException("파일 업로드 오류"); Relation relation = new Relation().setInfoType(Crdn.INF_TYPE).setInfoKey(crdnId); FileInfo modifyFileInfo = new FileInfoFactory().make(relation, modifyFile); modifyFileInfo.setSortOrder(beforeOrder); List> processList = new ArrayList<>(); processList.add(Map.of("process", "delete", "key", beforeKey)); processList.add(Map.of("process", "insert", "obj", modifyFileInfo)); Crdn crdn = new Crdn(); crdn.setCrdnId(crdnId); crdn.setAtchFileCnt(atchFileCnt); String retMessage = crdnService.changeCrdnImageFile(crdn, processList); return new ModelAndView("jsonView") .addObject("saved", retMessage.contains("[S]")) .addObject("retMessage", retMessage); } @PostMapping(name= "단속 사진 삭제", value = "/deleteCrdnImageFile.do") public ModelAndView deleteCrdnImageFile(String crdnId, int atchFileCnt, String[] deleteKeys) { if(deleteKeys == null || deleteKeys.length == 0 || deleteKeys[0] == null || deleteKeys[0].isEmpty()) throw new RuntimeException("파일 삭제 오류"); List> processList = new ArrayList<>(); for(String deleteKey : deleteKeys) { processList.add(Map.of("process", "delete", "key", deleteKey)); } Crdn crdn = new Crdn(); crdn.setCrdnId(crdnId); crdn.setAtchFileCnt(atchFileCnt); String retMessage = crdnService.changeCrdnImageFile(crdn, processList); return new ModelAndView("jsonView") .addObject("saved", retMessage.contains("[S]")) .addObject("retMessage", retMessage); } @PostMapping(name= "단속 사진 순서 변경", value = "/changeOrderCrdnImageFile.do") public ModelAndView changeOrderCrdnImageFile(String crdnId, int atchFileCnt, String[] orderedKeys) { if(orderedKeys == null || orderedKeys.length == 0 || orderedKeys[0] == null || orderedKeys[0].isEmpty()) throw new RuntimeException("파일 순서 변경 오류"); List> processList = new ArrayList<>(); for(String orderedKey : orderedKeys) { processList.add(Map.of("process", "order", "key", orderedKey)); } Crdn crdn = new Crdn(); crdn.setCrdnId(crdnId); crdn.setAtchFileCnt(atchFileCnt); String retMessage = crdnService.changeCrdnImageFile(crdn, processList); return new ModelAndView("jsonView") .addObject("saved", retMessage.contains("[S]")) .addObject("retMessage", retMessage); } @RequestMapping(name= "단속 사진 목록", value = "/fileList.do") public ModelAndView getFileList(FileQuery req) { String infoType = ifEmpty(req.getInfoType(), () -> ""); boolean singleKey = (req.getInfoKeys() != null && req.getInfoKeys().size() == 1); //파일 목록 List fileInfoList = fileService.getFileList(req); for(DataObject fileInfo : fileInfoList) { List mosaicInfos = mediaBean.getMosaicList(fileInfo.string("FILE_ID")); if(mosaicInfos != null) { String mosaic = ""; for(int i=0; i < mosaicInfos.size(); i++) { DataObject mosaicInfo = mosaicInfos.get(i); if(i != 0) { mosaic += "|"; } mosaic += mosaicInfo.string("X_AXS") + "," + mosaicInfo.string("Y_AXS") + "," + mosaicInfo.string("MOSC_LEN") + "," + mosaicInfo.string("MOSC_HGT"); } fileInfo.set("MOSAIC", mosaic); } else { fileInfo.set("MOSAIC", ""); } } //민원 동영상파일 첨부 여부 String cvlcptVideoExistYn = "N"; if(infoType.equals(Crdn.INF_TYPE) && singleKey) { DataObject dataObject = crdnInfoMapper.selectCrdnInfo(req.getInfoKeys().get(0)); String linkTblNm = dataObject.string("LINK_TBL_NM"); if(!linkTblNm.equals("")) { //민원연계자료 FileQuery cvlcptFileQuery = new FileQuery(); if(linkTblNm.equals("TB_ESB_INTERFACE")) { cvlcptFileQuery.setInfoType("010"); } else if(linkTblNm.equals("TB_SAEOL_CVLCPT_DTL")) { cvlcptFileQuery.setInfoType("020"); } else { throw new RuntimeException("파일 조회 중 오류가 발생하였습니다."); } cvlcptFileQuery.setInfoKeys(dataObject.string("LINK_ID")); List linkFileList = fileBean.getFileList(cvlcptFileQuery); if(linkFileList != null && !linkFileList.isEmpty()) { for(int i=0; i < linkFileList.size(); i++) { String mimeType = linkFileList.get(i).string("MIME_TYPE"); if(Arrays.asList(Sprt.VIDEO_MIME_TYPE).contains(mimeType)) { cvlcptVideoExistYn = "Y"; } } } } } return setPagingInfo(new ModelAndView("jsonView"), fileInfoList, "file") .addObject("cvlcptVideoExistYn", cvlcptVideoExistYn); } }