You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

527 lines
20 KiB
Java

package cokr.xit.fims.crdn.web;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
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.file.FileInfo;
import cokr.xit.base.file.FileInspector;
import cokr.xit.base.file.web.FileInfoFactory;
import cokr.xit.base.user.dao.UserMapper;
import cokr.xit.base.web.ApplicationController;
import cokr.xit.fims.FimsConf;
import cokr.xit.fims.base.FimsUser;
import cokr.xit.fims.base.service.bean.OgdpBean;
import cokr.xit.fims.cmmn.CmmnUtil;
import cokr.xit.fims.cmmn.ftp.FTPUtil;
import cokr.xit.fims.cmmn.ftp.RemoteSystemInfo;
import cokr.xit.fims.cmmn.service.bean.StngBean;
import cokr.xit.fims.crdn.Crdn;
import cokr.xit.fims.crdn.CrdnQuery;
import cokr.xit.fims.crdn.dao.CrdnInstMapper;
import cokr.xit.fims.crdn.dao.CrdnStngMapper;
import cokr.xit.fims.crdn.receive.eqpmnt.AttachedTxtParser;
import cokr.xit.fims.crdn.receive.eqpmnt.LayoutDescriptor;
import cokr.xit.fims.crdn.receive.eqpmnt.LayoutDiscriminator;
import cokr.xit.fims.crdn.receive.eqpmnt.LayoutParser;
import cokr.xit.fims.crdn.receive.eqpmnt.OnlyImageParser;
import cokr.xit.fims.crdn.receive.eqpmnt.SingleFileParser;
import cokr.xit.fims.crdn.receive.eqpmnt.dao.CrdnEqpmntMapper;
import cokr.xit.fims.crdn.receive.eqpmnt.service.bean.CrdnEqpmntBean;
import cokr.xit.fims.crdn.service.CrdnService;
import cokr.xit.fims.crdn.service.CrdnStngService;
import cokr.xit.fims.crdn.service.ImportService;
import cokr.xit.fims.crdn.service.bean.CrdnStngBean;
import cokr.xit.fims.mngt.service.bean.TaskProcessor;
import cokr.xit.fims.task.Task;
import cokr.xit.foundation.UserInfo;
import cokr.xit.foundation.data.DataObject;
/**단속 자료 등록 서비스 웹 컨트롤러.<br />
* {웹 컨텍스트}/crdn/crdn05 로 접근할 수 있다.
* @author leebj
*/
@Controller
@RequestMapping(name="단속 자료 등록", value=Crdn05Controller.CLASS_URL)
public class Crdn05Controller extends ApplicationController {
public static final String CLASS_URL = "/crdn/crdn05";
public class METHOD_URL {
public static final String
crackdownDataRegistrationMain = "/010/main.do",
getTodayCrdnDataList = "/010/list.do",
getFileRegistrationScreen = "/020/info.do",
importFileFromServer = "/020/importFileFromServer.do",
importFileFromClient = "/020/importFileFromClient.do",
getEquipmentFileInfoList = "/020/list.do",
removeLinkFile = "/020/remove.do",
createCrdnByLinkFile = "/020/create.do",
getManualRegistrationScreen = "/030/info.do",
createCrdnDataByManual = "/030/create.do"
;
}
@Resource(name="importService")
private ImportService importService;
@Resource(name="crdnService")
private CrdnService crdnService;
@Resource(name="crdnStngService")
private CrdnStngService crdnStngService;
@Resource(name="crdnStngBean")
private CrdnStngBean crdnStngBean;
@Resource(name="crdnInstMapper")
private CrdnInstMapper crdnInstMapper;
@Resource(name="userMapper")
protected UserMapper userMapper;
@Resource(name="crdnStngMapper")
private CrdnStngMapper crdnStngMapper;
@Resource(name = "ogdpBean")
private OgdpBean ogdpBean;
@Resource(name = "stngBean")
private StngBean stngBean;
@Resource(name = "crdnEqpmntBean")
private CrdnEqpmntBean crdnEqpmntBean;
@Resource(name = "crdnEqpmntMapper")
private CrdnEqpmntMapper crdnEqpmntMapper;
/** 단속 자료 등록 메인화면을 연다.
* @return fims/crdn/crdn05010-main
*/
@RequestMapping(name="단속 자료 등록 메인", value=METHOD_URL.crackdownDataRegistrationMain)
public ModelAndView crackdownDataRegistrationMain() {
Map<String, List<CommonCode>> commonCodes = getCodesOf("FIM003", "FIM026", "FIM054");
return addCodes(
commonCodes,
new ModelAndView("fims/crdn/crdn05010-main")
.addObject("pageName", "crdn05010")
.addObject("FIM054List", commonCodes.get("FIM054"))
.addObject("TaskListForSgg", stngBean.filterTaskSectionCodeForSgg(commonCodes.get("FIM054"))),
"FIM003", "FIM026"
);
}
/**당일 등록한 단속자료 목록을 조회한다.
* {@link CrdnService#getCrackdownList(CrdnQuery)} 참고
* @param query 단속자료 목록 조회 조건
* @return jsonView
*/
@Task
@RequestMapping(name="당일 입력 단속자료 목록 조회", value=METHOD_URL.getTodayCrdnDataList)
public ModelAndView getTodayCrdnDataList(CrdnQuery query) {
UserInfo currentUser = currentUser();
setFetchSize(query)
.setGridType("todayInsert")
.setCurrentUserId(currentUser.getId())
.setSggCd(currentUser.getOrgID());
return setPagingInfo(new ModelAndView("jsonView"), crdnService.getCrackdownList(query), "");
}
/**단속자료 파일 등록 팝업화면을 반환한다.
* @param
* @return fims/crdn/crdn05020-info
*/
@Task
@RequestMapping(name="단속자료 파일 등록 화면", value=METHOD_URL.getFileRegistrationScreen)
public ModelAndView getFileRegistrationScreen(HttpServletRequest hreq) {
Map<String, List<CommonCode>> commonCodes = getCodesOf("FIM054");
List<DataObject> dirs = new FileInspector().getDirList(FimsConf.get().getViolationFileDir());
dirs.sort((r1, r2) -> r2.string("name").compareTo(r1.string("name")));
return new ModelAndView("fims/crdn/crdn05020-info")
.addObject("pageName", "crdn05020")
.addObject("taskSeCd", hreq.getParameter("taskSeCd"))
.addObject("FIM054List", commonCodes.get("FIM054"))
.addObject("TaskListForSgg", stngBean.filterTaskSectionCodeForSgg(commonCodes.get("FIM054")))
.addObject("dirList", toJson(dirs));
}
@RequestMapping(name="cctv 단속파일 목록", value="/020/files.do")
public ModelAndView getDirFiles(String dir) {
List<DataObject> fileList = new FileInspector().getFileList(FimsConf.get().violationFileDir(dir));
return new ModelAndView("jsonView")
.addObject("fileList", fileList);
}
/** FTP 서버로부터 단속연계파일을 가져온다.
* @param
* @return
*/
@Task
@RequestMapping(name="단속연계파일 가져오기(FTP)", value=METHOD_URL.importFileFromServer)
public ModelAndView importFileFromServer(HttpServletRequest hreq) {
FimsUser currentUser = (FimsUser) currentUser().getUser();
List<LayoutDescriptor> layoutDescriptors = crdnStngService.getLinkFileLayoutMetadata(currentUser.getOrgID(), hreq.getParameter("taskSeCd"), currentUser().getInstitute(), currentUser.getDeptCode());
ModelAndView mav = new ModelAndView("jsonView");
for(int i=0; i < layoutDescriptors.size(); i++) {
LayoutDescriptor layoutDescriptor = layoutDescriptors.get(i);
DataObject remoteInfo = crdnEqpmntMapper.selectRemoteInfo(layoutDescriptor.getFileLayoutId());
if (remoteInfo == null || isEmpty(remoteInfo.string("REMOTE_IP")))
continue;
RemoteSystemInfo rs = new RemoteSystemInfo();
rs.setIp(remoteInfo.string("REMOTE_IP"));
rs.setPort(remoteInfo.string("REMOTE_PORT"));
rs.setId(remoteInfo.string("REMOTE_ID"));
rs.setPw(remoteInfo.string("REMOTE_PASSWORD"));
rs.setOsType(remoteInfo.string("REMOTE_OS"));
String remoteWorkPath = remoteInfo.string("REMOTE_WORK_PATH");
String workPath = layoutDescriptor.getLinkFileLocation();
ensureDir(workPath);
try {
boolean result = FTPUtil.fileDown(rs, remoteWorkPath, workPath);
if (!result) {
return mav.addObject("saved", false);
}
} catch (Exception e) {
return mav.addObject("saved", false);
}
CmmnUtil.removeDuplicateFileName(workPath);
}
return mav.addObject("saved", true);
}
/** 사용자 클라이언트(브라우저)로부터 단속연계파일을 가져온다.
* @param
* @return
*/
@Task
@RequestMapping(name="단속연계파일 가져오기(파일업로드)", value=METHOD_URL.importFileFromClient)
public ModelAndView importFileFromClient(HttpServletRequest hreq, MultipartFile[] uploadFiles) {
UserInfo currentUser = currentUser();
List<LayoutDescriptor> layoutDescriptors = crdnStngService.getLinkFileLayoutMetadata(
currentUser.getOrgID(),
hreq.getParameter("taskSeCd"),
currentUser.getInstitute(),
currentUser.getDeptCode()
);
LayoutDescriptor layoutDescriptor =
layoutDescriptors.size() == 1 ? layoutDescriptors.get(0) :
layoutDescriptors.size() > 1 ? LayoutDiscriminator.choice(uploadFiles, layoutDescriptors) :
new LayoutDescriptor();
String workPath = layoutDescriptor.getLinkFileLocation();
ensureDir(workPath);
ModelAndView mav = new ModelAndView("jsonView");
try {
for (MultipartFile upload: uploadFiles) {
File newFile = new File(workPath+File.separator+upload.getOriginalFilename());
upload.transferTo(newFile);
}
} catch (Exception e) {
return mav.addObject("saved", false);
}
CmmnUtil.removeDuplicateFileName(workPath);
return mav.addObject("saved", true);
}
/**전송,업로드 완료된 장비업체의 단속연계파일 정보를 조회한다.
* @param
* @return
*/
@Task
@RequestMapping(name="장비업체 단속파일 목록 조회", value=METHOD_URL.getEquipmentFileInfoList)
public ModelAndView getEquipmentFileInfoList(HttpServletRequest hreq) {
UserInfo currentUser = currentUser();
List<LayoutDescriptor> layoutDescriptors = crdnStngService.getLinkFileLayoutMetadata(
currentUser.getOrgID(),
hreq.getParameter("taskSeCd"),
currentUser.getInstitute(),
currentUser.getDeptCode()
);
if (isEmpty(layoutDescriptors))
throw new RuntimeException("연계파일 레이아웃 정보 조회에 실패하였습니다.");
int nextTempGroupSeq = 1;
List<DataObject> allParsedDataList = new ArrayList<DataObject>();
TaskProcessor taskProcessor = TaskProcessor.get();
Map<String, List<CommonCode>> codeInfo = getCodesOf("FIM004","FIM005","FIM006","FIM007","FIM061");
for(LayoutDescriptor layoutDescriptor: layoutDescriptors) {
String workPath = layoutDescriptor.getLinkFileLocation();
ensureDir(workPath);
List<Path> fileList = null;
try { //폴더는 제외하고 파일만 필터링
fileList = Files.walk(Paths.get(workPath)).filter(Files::isRegularFile).toList();
} catch (IOException e) {
throw new RuntimeException("파일 조회 오류." + e);
}
if (isEmpty(fileList)) continue;
LayoutParser parser = switch (layoutDescriptor.getFileGroup()){
case "TXT" -> new AttachedTxtParser();
case "JPG" -> new OnlyImageParser();
case "BIN" -> new SingleFileParser();
default -> null;
};
parser.setTaskProcessor(taskProcessor);
parser.setTempGroupSeq(nextTempGroupSeq);
parser.addCommonCode(codeInfo);
parser.setDescriptor(layoutDescriptor);
List<DataObject> parsedDataList = parser.parsing(fileList);
allParsedDataList.addAll(parsedDataList);
nextTempGroupSeq = parser.getTempGroupSeq() + 1;
}
return setPagingInfo(new ModelAndView("jsonView"), allParsedDataList,"");
}
/**단속 연계 파일을 삭제한다.
* @param hreq 삭제 요청
* @return jsonView
* <pre><code> {
* "saved": 등록되었으면 true, 그렇지 않으면 false
* }</code></pre>
*/
@Task
@RequestMapping(name="장비업체 단속파일 삭제", value=METHOD_URL.removeLinkFile)
public ModelAndView removeLinkFile(HttpServletRequest hreq) {
LayoutDescriptor info = crdnEqpmntBean.getLinkFileLayoutMetadata(hreq.getParameter("fileLayoutId"));
String workPath = info.getLinkFileLocation();
String fileName = hreq.getParameter("fileName");
String rtnMsg = crdnService.removeEquipmentLinkFile(workPath, fileName);
return new ModelAndView("jsonView")
.addObject("saved", rtnMsg.contains("[S]"));
}
/**단속 연계 파일로 단속자료를 생성한다.
* @param taskSeCd 업무 구분, fileGroupType 파일그룹타입, linkFileInfos 연계파일정보
* @return jsonView
* <pre><code> {
* "saved": 등록되었으면 true, 그렇지 않으면 false
* }</code></pre>
*/
@Task
@RequestMapping(name="장비업체 단속파일로 단속자료 생성", value=METHOD_URL.createCrdnByLinkFile)
public ModelAndView createCrdnByLinkFile(String taskSeCd, String fileLayoutId, String fileGroupType, String[] linkFileInfos) {
Map<String, String> processInfo = new HashMap<String,String>();
processInfo.put("institute", currentUser().getInstitute());
processInfo.put("taskSeCd", taskSeCd);
processInfo.put("fileGroupType", fileGroupType);
String workPath = crdnEqpmntBean.getLinkFileLayoutMetadata(fileLayoutId).getLinkFileLocation();
processInfo.put("workPath", workPath);
List<DataObject> linkFileInfoList = Stream.of(linkFileInfos)
.filter(linkFileInfo -> !linkFileInfo.equals("{}"))
.map(linkFileInfo -> fromJson(linkFileInfo, DataObject.class))
.toList();
Map<String, Object> result = importService.createCrdnByEquipmentLinkFile(processInfo, linkFileInfoList);
return new ModelAndView("jsonView")
.addAllObjects(result);
/*
boolean saved = (boolean) result.get("saved");
ModelAndView mav = new ModelAndView("jsonView")
.addObject("saved", saved);
if (!isEmpty(result.get("alertMessage")))
mav.addObject("alertMessage", result.get("alertMessage"));
if (!saved)
mav.addObject("failReason", ifEmpty(result.get("failReason"), () -> "알 수 없는 오류"));
return mav;
*/
}
@PostMapping(name="장비업체 단속파일로 단속자료 생성", value="/020/upload.do")
public ModelAndView createCrdnFromLinkFile(HttpServletRequest hreq, MultipartFile[] uploadFiles) {
UserInfo currentUser = currentUser();
String sggCd = currentUser.getOrgID(),
taskSeCd = hreq.getParameter("taskSeCd"),
instCd = currentUser.getInstitute(),
deptCode = currentUser.getDeptCode();
List<LayoutDescriptor> descriptors = crdnStngService.getLinkFileLayoutMetadata(sggCd, taskSeCd, instCd, deptCode);
if (isEmpty(descriptors))
throw new RuntimeException("연계파일 레이아웃 정보 조회에 실패하였습니다.");
int nextTempGroupSeq = 1;
TaskProcessor taskProcessor = TaskProcessor.get();
HashMap<String, String> processInfo = new HashMap<>();
processInfo.put("institute", currentUser().getInstitute());
processInfo.put("taskSeCd", taskSeCd);
processInfo.put("excludeExempted", hreq.getParameter("doExempt"));
Map<String, LayoutParser> parsers = Map.of(
"TXT", new AttachedTxtParser(),
"JPG", new OnlyImageParser(),
"BIN", new SingleFileParser()
);
Map<String, List<CommonCode>> codeInfo = getCodesOf("FIM004","FIM005","FIM006","FIM007","FIM061");
List<DataObject> parsed = new ArrayList<DataObject>();
for (LayoutDescriptor descriptor: descriptors) {
LayoutParser parser = parsers.get(descriptor.getFileGroup());
parser.setTaskProcessor(taskProcessor);
parser.setTempGroupSeq(nextTempGroupSeq);
parser.addCommonCode(codeInfo);
parser.setDescriptor(descriptor);
if (!isEmpty(uploadFiles)) { // 사용자 pc 파일 업로드
String workPath = crdnEqpmntBean.getLinkFileLayoutMetadata(descriptor.getFileLayoutId()).getLinkFileLocation();
processInfo.put("workPath", workPath);
parsed.addAll(parser.parse(workPath, List.of(uploadFiles)));
} else { // 서버 cctv 디렉토리
String[] dirs = blankIfEmpty(hreq.getParameter("dirs")).split(",");
for (String dir: dirs) {
String srcDir = FimsConf.get().violationFileDir(dir);
List<String> filenames = List.of(blankIfEmpty(hreq.getParameter("filenames")).split(","));
log().debug("{} filenames received", filenames.size());
Path workDir = ensureDir(srcDir + File.separator + "work").toPath();
try {
// 파일들을 작업 디렉토리로 이동
List<Path> filepaths = Files.list(Paths.get(srcDir))
.filter(path -> contains(path.toString(), filenames, false))
.toList();
log().debug("{} filepaths extracted", filepaths.size());
filepaths.forEach(src -> {
try {
Files.move(src, workDir.resolve(src.getFileName()), StandardCopyOption.REPLACE_EXISTING);
} catch (Exception e) {
throw runtimeException(e);
}
});
List<File> files = Files.list(workDir)
.filter(path -> contains(path.toString(), filenames, true))
.map(path -> path.toFile())
.toList();
log().debug("{} files parsed", files.size());
parsed.addAll(parser.parse(files));
} catch (Exception e) {
throw runtimeException(e);
}
}
}
nextTempGroupSeq = parser.getTempGroupSeq();
}
Map<String, Object> result = importService.createCrdnByEquipmentLinkFile(processInfo, parsed);
return new ModelAndView("jsonView")
.addAllObjects(result);
}
private boolean contains(String path, List<String> filenames, boolean inWork) {
if (!inWork) {
if (path.contains("work")
|| path.contains("success")
|| path.contains("fail")
|| path.contains("duplicates")
) return false;
}
for (String filename: filenames) {
if (path.endsWith(filename))
return true;
}
return false;
}
/**단속자료 수기 등록 팝업화면을 반환한다.
* @param
* @return fims/crdn/crdn05030-info
*/
@Task
@RequestMapping(name="단속자료 수기 등록 화면", value=METHOD_URL.getManualRegistrationScreen)
public ModelAndView getManualRegistrationScreen(HttpServletRequest hreq) {
String taskSeCd = hreq.getParameter("taskSeCd");
String sggCd = currentUser().getOrgID();
List<String> stdgNmList = crdnStngMapper.selectStdgNmList(sggCd);
ModelAndView mav = new ModelAndView("fims/crdn/crdn05030-info")
.addObject("pageName", "crdn05030")
.addObject("taskSeCd", taskSeCd)
.addObject("sggCd", sggCd)
.addObject("stdgNmList", stdgNmList)
.addObject("today", dateFormats.format("yyyyMMdd", System.currentTimeMillis()));
TaskProcessor taskProcessor = TaskProcessor.get();
String vltnByTask = taskProcessor.getVltnCdGrp(taskSeCd);
Map<String, List<CommonCode>> commonCodes = getCodesOf("FIM011","FIM053","LVS005",vltnByTask);
mav.addObject("FIM011List", commonCodes.get("FIM011"))
.addObject("FIM053List", commonCodes.get("FIM053"))
.addObject("LVS005List", commonCodes.get("LVS005"))
.addObject("VLTNList", commonCodes.get(vltnByTask));
CrdnQuery query = new CrdnQuery()
.setSggCd(sggCd)
.setTaskSeCd(taskSeCd);
List<DataObject> teamList = crdnStngService.getTeamList(query);
String[] extraCodeGroups = taskProcessor.getExtraCdGrps(taskSeCd);
if (!isEmpty(extraCodeGroups)) {
commonCodes = getCodesOf(extraCodeGroups);
commonCodes.forEach((codeGroup, codes) -> {
mav.addObject(codeGroup + "List", codes);
});
}
return mav.addObject("TeamList", teamList);
}
/**단속 대장을 수기 등록한다.
* @param hreq 등록 요청, crdn 단속 대장 정보, newFileList 업로드한 이미지 파일
* @return jsonView
* <pre><code> {
* "saved": 등록되었으면 true, 그렇지 않으면 false
* }</code></pre>
*/
@Task
@RequestMapping(name="단속자료 수기 등록", value=METHOD_URL.createCrdnDataByManual)
public ModelAndView createCrdnDataByManual(HttpServletRequest hreq, Crdn crdn, MultipartFile[] newFileList) {
UserInfo userInfo = currentUser();
ogdpBean.initUserInfo(userInfo);
crdn.setSggCd((String)userInfo.getInfo().get("sggCd"));
crdn.setCrdnRegSeCd("01");
crdn.setCrdnInptSeCd(((String)userInfo.getInfo().get("instNm")).endsWith("시") ? "03" : "02"); // 03: 시청, 02: 구청
if ("on".equals(hreq.getParameter("doWarning"))) // 계고 처리
crdn.setCrdnSttsCd("83");
if ("on".equals(hreq.getParameter("doExempt"))) // 면제차량
crdn.setCrdnSttsCd("81");
Map<String, Object> nonQueryRequest = new HashMap<String, Object>();
crdn.setCvlcptLinkYn("N");
List<FileInfo> fileInfoList = !isEmpty(newFileList) ? new FileInfoFactory().makeFileInfos(null, newFileList) : Collections.emptyList();
String rtnMsg = crdnService.create(nonQueryRequest, crdn, fileInfoList);
return new ModelAndView("jsonView")
.addObject("saved", rtnMsg.contains("[S]"));
}
}