Browse Source

feat: 드론원스톱 신청목록 엑셀다운로드

feature/address-coordinate
지대한 3 months ago
parent
commit
f9f450a9c2
  1. 6
      http/server/server.http
  2. 4
      pav-server/build.gradle
  3. 13
      pav-server/src/main/java/com/palnet/biz/api/bas/dos/controller/BasDosController.java
  4. 20
      pav-server/src/main/java/com/palnet/biz/api/bas/dos/model/BasDosPlanDownloadRq.java
  5. 5
      pav-server/src/main/java/com/palnet/biz/api/bas/dos/model/DosExcelCellStyleType.java
  6. 5
      pav-server/src/main/java/com/palnet/biz/api/bas/dos/model/PlanSelectType.java
  7. 232
      pav-server/src/main/java/com/palnet/biz/api/bas/dos/service/BasDosService.java
  8. 1
      pav-server/src/main/java/com/palnet/comn/code/ErrorCode.java
  9. 5
      pav-server/src/main/java/com/palnet/comn/utils/HttpUtils.java

6
http/server/server.http

@ -105,4 +105,8 @@ Content-Type: application/json
]
}
]
}
}
### dos(드론원스톱) excel download
GET {{appHost}}/api/bas/dos/plan/download/excel?searchStDt=2024-06-14&searchEndDt=2024-06-14
Authorization: {{accessToken}}

4
pav-server/build.gradle

@ -93,6 +93,10 @@ dependencies {
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr353:2.15.2'
// excel
implementation 'org.apache.poi:poi:5.2.5'
implementation 'org.apache.poi:poi-ooxml:5.2.5'
// geometry
implementation 'com.esri.geometry:esri-geometry-api:2.2.4'
implementation 'org.locationtech.proj4j:proj4j:1.3.0'

13
pav-server/src/main/java/com/palnet/biz/api/bas/dos/controller/BasDosController.java

@ -2,6 +2,7 @@ package com.palnet.biz.api.bas.dos.controller;
import com.palnet.biz.api.bas.dos.model.BasDosPlanRq;
import com.palnet.biz.api.bas.dos.model.BasDosPlanRs;
import com.palnet.biz.api.bas.dos.model.PlanSelectType;
import com.palnet.biz.api.bas.dos.model.UpdatePlanRq;
import com.palnet.biz.api.bas.dos.service.BasDosService;
import com.palnet.comn.code.ErrorCode;
@ -32,7 +33,7 @@ public class BasDosController {
*/
@GetMapping("/plan")
public ResponseEntity<?> getDosPlan(BasDosPlanRq rq) {
List<BasDosPlanRs> rs = basDosService.getDosPlan(rq);
List<BasDosPlanRs> rs = basDosService.getDosPlan(rq, PlanSelectType.LIST);
return ResponseEntity.ok(rs);
}
@ -51,4 +52,14 @@ public class BasDosController {
return ResponseEntity.ok().build();
}
/**
* 엑셀 다운로드
* @param rq
*/
@GetMapping("/plan/download/excel")
public void downloadExcel(BasDosPlanRq rq) {
List<BasDosPlanRs> rs = basDosService.getDosPlan(rq, PlanSelectType.DOWNLOAD);
basDosService.createExcel(rs);
}
}

20
pav-server/src/main/java/com/palnet/biz/api/bas/dos/model/BasDosPlanDownloadRq.java

@ -0,0 +1,20 @@
package com.palnet.biz.api.bas.dos.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDate;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class BasDosPlanDownloadRq {
private LocalDate searchStDt;
private LocalDate searchEndDt;
private String applyNo;
private String approvalCd;
private String selectZone;
}

5
pav-server/src/main/java/com/palnet/biz/api/bas/dos/model/DosExcelCellStyleType.java

@ -0,0 +1,5 @@
package com.palnet.biz.api.bas.dos.model;
public enum DosExcelCellStyleType {
HEADER_CELL, BODY_CELL
}

5
pav-server/src/main/java/com/palnet/biz/api/bas/dos/model/PlanSelectType.java

@ -0,0 +1,5 @@
package com.palnet.biz.api.bas.dos.model;
public enum PlanSelectType {
LIST, DOWNLOAD
}

232
pav-server/src/main/java/com/palnet/biz/api/bas/dos/service/BasDosService.java

@ -1,9 +1,7 @@
package com.palnet.biz.api.bas.dos.service;
import com.palnet.biz.api.bas.dos.model.BasDosPlanAreaRs;
import com.palnet.biz.api.bas.dos.model.BasDosPlanRq;
import com.palnet.biz.api.bas.dos.model.BasDosPlanRs;
import com.palnet.biz.api.bas.dos.model.UpdatePlanRq;
import com.palnet.biz.api.bas.dos.model.*;
import com.palnet.biz.api.external.model.ApprovalCd;
import com.palnet.biz.api.external.model.DosApprovalResult;
import com.palnet.biz.api.external.service.DronOneStopService;
import com.palnet.biz.jpa.entity.DosFltPlanArea;
@ -12,16 +10,26 @@ import com.palnet.biz.jpa.entity.DosFltPlanResult;
import com.palnet.biz.jpa.repository.dos.DosFltPlanAreaRepository;
import com.palnet.biz.jpa.repository.dos.DosFltPlanQueryRepository;
import com.palnet.biz.jpa.repository.dos.DosFltPlanResultRepository;
import com.palnet.comn.code.ErrorCode;
import com.palnet.comn.exception.CustomException;
import com.palnet.comn.utils.AirspaceUtils;
import com.palnet.comn.utils.AreaUtils;
import com.palnet.comn.utils.HttpUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFColor;
import org.apache.poi.xssf.usermodel.XSSFFont;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.Instant;
import java.time.LocalDate;
import java.util.ArrayList;
@ -50,7 +58,7 @@ public class BasDosService {
* @return List<BasDosPlanRs>
*/
@Transactional(readOnly = true)
public List<BasDosPlanRs> getDosPlan(BasDosPlanRq rq) {
public List<BasDosPlanRs> getDosPlan(BasDosPlanRq rq, PlanSelectType type) {
List<DosFltPlanBas> planBasList = dosFltPlanQueryRepository.findPlanByBasSearch(rq);
@ -110,22 +118,24 @@ public class BasDosService {
continue;
}
List<Coordinate> coordBuffers = areaUtils.createCircle(new Coordinate(area.getLon(), area.getLat()), area.getBufferZone());
if ("GIMPO".equalsIgnoreCase(rq.getSelectZone())) {
AirspaceUtils airspaceUtils = AirspaceUtils.getInstance();
Geometry rqGeometry = airspaceUtils.createGeometryByCoordinate(coordBuffers);
List<Map<String, Double>> bufferCoordList = null;
if (type == PlanSelectType.LIST) {
List<Coordinate> coordBuffers = areaUtils.createCircle(new Coordinate(area.getLon(), area.getLat()), area.getBufferZone());
AirspaceUtils.FeatureInfo targetfeatureInfo = new AirspaceUtils.FeatureInfo(null, null, 0.0, area.getFltElev(), rqGeometry);
boolean isDuplicatedAirspace = airspaceUtils.isDuplicatedAirspace(targetfeatureInfo, AirspaceUtils.AirspaceType.GIMPO);
if (!isDuplicatedAirspace) {
continue;
if ("GIMPO".equalsIgnoreCase(rq.getSelectZone())) {
AirspaceUtils airspaceUtils = AirspaceUtils.getInstance();
Geometry rqGeometry = airspaceUtils.createGeometryByCoordinate(coordBuffers);
AirspaceUtils.FeatureInfo targetfeatureInfo = new AirspaceUtils.FeatureInfo(null, null, 0.0, area.getFltElev(), rqGeometry);
boolean isDuplicatedAirspace = airspaceUtils.isDuplicatedAirspace(targetfeatureInfo, AirspaceUtils.AirspaceType.GIMPO);
if (!isDuplicatedAirspace) {
continue;
}
}
bufferCoordList = coordBuffers.stream().map(coord -> Map.of("lat", coord.y, "lon", coord.x)).collect(Collectors.toList());
}
List<Map<String, Double>> bufferCoordList = coordBuffers.stream().map(coord -> Map.of("lat", coord.y, "lon", coord.x)).collect(Collectors.toList());
// TODO 추후 Utils 생성
// 주소 분할 - 임시 순서대로 split
String addr = area.getAddr();
@ -231,7 +241,7 @@ public class BasDosService {
if (planRq.getFltElev() != null || planRq.getBufferZone() != null) {
Optional<DosFltPlanResult> optionalResult = dosFltPlanResultRepository.findById(area.getPlanAreaSno());
DosFltPlanResult dosFltPlanResult = null;
if(optionalResult.isPresent()) {
if (optionalResult.isPresent()) {
dosFltPlanResult = optionalResult.get();
}
// Circle만 체크
@ -244,7 +254,7 @@ public class BasDosService {
DosApprovalResult approvalResult = dronOneStopService.getApprovalResult(bufferZone, fltElev, centerPoint);
// 재검증 수정
if(dosFltPlanResult != null){
if (dosFltPlanResult != null) {
dosFltPlanResult.setApprovalCd(approvalResult.getApprovalCd().getCode());
dosFltPlanResult.setFltElevMax(approvalResult.getFltElevMax());
} else {
@ -286,4 +296,190 @@ public class BasDosService {
}).collect(Collectors.toList());
}
public void createExcel(List<BasDosPlanRs> rs) {
try (XSSFWorkbook workbook = new XSSFWorkbook()) {
Sheet sheet = workbook.createSheet("sheet1");
int cellWidth53 = getCellWidthByPixel(53);
int cellWidth70 = getCellWidthByPixel(70);
int cellWidth73 = getCellWidthByPixel(73);
int cellWidth82 = getCellWidthByPixel(82);
int cellWidth113 = getCellWidthByPixel(113);
int cellWidth145 = getCellWidthByPixel(145);
int cellWidth174 = getCellWidthByPixel(174);
sheet.setColumnWidth(0, cellWidth53);
sheet.setColumnWidth(1, cellWidth53);
sheet.setColumnWidth(2, cellWidth53);
sheet.setColumnWidth(3, cellWidth53);
sheet.setColumnWidth(4, cellWidth53);
sheet.setColumnWidth(5, cellWidth113);
sheet.setColumnWidth(6, cellWidth53);
sheet.setColumnWidth(7, cellWidth70);
sheet.setColumnWidth(8, cellWidth174);
sheet.setColumnWidth(9, cellWidth73);
sheet.setColumnWidth(10, cellWidth82);
sheet.setColumnWidth(11, cellWidth145);
Row row = null;
Cell cell = null;
int rowNum = 1;
// Header
XSSFCellStyle headerStyle = getDefaultCellStyle(workbook, DosExcelCellStyleType.HEADER_CELL);
XSSFCellStyle firstHeaderStyle = getDefaultCellStyle(workbook, DosExcelCellStyleType.HEADER_CELL);
firstHeaderStyle.setBorderLeft(BorderStyle.MEDIUM);
XSSFCellStyle lastHeaderStyle = getDefaultCellStyle(workbook, DosExcelCellStyleType.HEADER_CELL);
lastHeaderStyle.setBorderRight(BorderStyle.MEDIUM);
row = sheet.createRow(rowNum++);
row.setHeightInPoints(33); // 높이를 포인트 단위로 설정
cell = row.createCell(0);
cell.setCellValue("월");
cell.setCellStyle(firstHeaderStyle);
cell = row.createCell(11);
cell.setCellValue("비고");
cell.setCellStyle(lastHeaderStyle);
cell = row.createCell(1);
cell.setCellValue("일");
cell.setCellStyle(headerStyle);
cell = row.createCell(2);
cell.setCellValue("신청자");
cell.setCellStyle(headerStyle);
cell = row.createCell(3);
cell.setCellValue("행정구역1");
cell.setCellStyle(headerStyle);
cell = row.createCell(4);
cell.setCellValue("행정구역2");
cell.setCellStyle(headerStyle);
cell = row.createCell(5);
cell.setCellValue("상세주소");
cell.setCellStyle(headerStyle);
cell = row.createCell(6);
cell.setCellValue("비행반경");
cell.setCellStyle(headerStyle);
cell = row.createCell(7);
cell.setCellValue("최고비행\n해발고도(m)");
cell.setCellStyle(headerStyle);
cell = row.createCell(8);
cell.setCellValue("세부사항");
cell.setCellStyle(headerStyle);
cell = row.createCell(9);
cell.setCellValue("비행목적");
cell.setCellStyle(headerStyle);
cell = row.createCell(10);
cell.setCellValue("긴급구조기관");
cell.setCellStyle(headerStyle);
XSSFCellStyle bodyStyle = getDefaultCellStyle(workbook, DosExcelCellStyleType.BODY_CELL);
XSSFCellStyle firstBodyStyle = getDefaultCellStyle(workbook, DosExcelCellStyleType.BODY_CELL);
firstHeaderStyle.setBorderLeft(BorderStyle.MEDIUM);
XSSFCellStyle lastBodyStyle = getDefaultCellStyle(workbook, DosExcelCellStyleType.BODY_CELL);
lastBodyStyle.setBorderRight(BorderStyle.MEDIUM);
XSSFCellStyle bodyStyleFontRed = getDefaultCellStyle(workbook, DosExcelCellStyleType.BODY_CELL);
bodyStyleFontRed.getFont().setColor(IndexedColors.RED.getIndex());
// Body
for (BasDosPlanRs plan : rs) {
for (BasDosPlanAreaRs area : plan.getAreaList()) {
ApprovalCd approvalCd = ApprovalCd.fromCode(area.getApprovalCd());
row = sheet.createRow(rowNum++);
cell = row.createCell(0);
cell.setCellValue(plan.getApplyDtMonth() != null ? plan.getApplyDtMonth() + "월" : "");
cell.setCellStyle(firstBodyStyle);
cell = row.createCell(1);
cell.setCellValue(plan.getApplyDtDay() != null ? plan.getApplyDtDay() + "일" : "");
cell.setCellStyle(bodyStyle);
cell = row.createCell(2);
cell.setCellValue(plan.getApplyNm() != null ? plan.getApplyNm() : "");
cell.setCellStyle(bodyStyle);
cell = row.createCell(3);
cell.setCellValue(area.getAddr1() != null ? area.getAddr1() : "");
cell.setCellStyle(bodyStyle);
cell = row.createCell(4);
cell.setCellValue(area.getAddr2() != null ? area.getAddr2() : "");
cell.setCellStyle(bodyStyle);
cell = row.createCell(5);
cell.setCellValue(area.getAddr3() != null ? area.getAddr3() : "");
cell.setCellStyle(bodyStyle);
cell = row.createCell(6);
cell.setCellValue(area.getFltElev() != null ? area.getFltElev() + "" : "");
cell.setCellStyle(bodyStyle);
cell = row.createCell(7);
cell.setCellValue(area.getFltElevMax() != null ? area.getFltElevMax() + "" : "불가");
if (approvalCd == ApprovalCd.UNAPPROVED) {
cell.setCellStyle(bodyStyleFontRed);
} else {
cell.setCellStyle(bodyStyle);
}
cell = row.createCell(8);
cell.setCellValue(area.getDtl() != null ? area.getDtl() : "");
cell.setCellStyle(bodyStyle);
cell = row.createCell(9);
cell.setCellValue(plan.getPurpose() != null ? plan.getPurpose() : "");
cell.setCellStyle(bodyStyle);
cell = row.createCell(10);
cell.setCellValue(area.getEra() != null ? area.getEra() : "");
cell.setCellStyle(bodyStyle);
cell = row.createCell(11);
cell.setCellValue(area.getRm() != null ? area.getRm() : "");
cell.setCellStyle(lastBodyStyle);
}
}
HttpServletResponse response = HttpUtils.getResponse();
// 컨텐츠 타입과 파일명 지정
response.setContentType("ms-vnd/excel");
response.setHeader("Content-Disposition", "attachment;filename=example.xlsx");
// Excel File Output
workbook.write(response.getOutputStream());
} catch (IOException e) {
throw new CustomException(ErrorCode.FILE_CREATE_FAIL);
}
}
private XSSFCellStyle getDefaultCellStyle(XSSFWorkbook workbook, DosExcelCellStyleType type) {
XSSFCellStyle style = workbook.createCellStyle();
// font
XSSFFont font = workbook.createFont();
font.setFontName("맑은 고딕");
font.setFontHeightInPoints((short) 10);
if (type == DosExcelCellStyleType.HEADER_CELL) {
font.setBold(true);
}
style.setFont(font);
// border
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
// color
if (type == DosExcelCellStyleType.HEADER_CELL) {
XSSFColor myColor = new XSSFColor(new java.awt.Color(233, 224, 208), null); // 예: 빨간색
style.setFillForegroundColor(myColor);
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
style.setBorderTop(BorderStyle.MEDIUM);
style.setBorderBottom(BorderStyle.MEDIUM);
}
// alignment
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
return style;
}
private int getCellWidthByPixel(int pixelWidth) {
// return (pixelWidth * 256) / 8;
return (int) ((pixelWidth / 6.0) * 256);
}
}

1
pav-server/src/main/java/com/palnet/comn/code/ErrorCode.java

@ -19,6 +19,7 @@ public enum ErrorCode {
PLAN_LAANC_NOT_VALID("FT502", "LAANC를 통과하지 못한 비행계획서입니다."),
EXTERNAL_API_ERROR("EA500", "외부서버 호출에 실패하였습니다."),
AUTH_NAUTHORIZED("AU001", "권한이 없습니다."),
FILE_CREATE_FAIL("FI001", "파일을 생성에 실패하였습니다."),
LIMIT_CALL("LT001", "호출횟수가 초과되었습니다."),

5
pav-server/src/main/java/com/palnet/comn/utils/HttpUtils.java

@ -235,6 +235,11 @@ public class HttpUtils {
ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
return sra.getRequest();
}
public static HttpServletResponse getResponse() {
ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
return sra.getResponse();
}
/**
* 클라이언트 IP 가져온다

Loading…
Cancel
Save