Browse Source

Merge commit '92be3047ce0305adc9dc861570276c845dd08995' into feature/main/drone-flight-statistics

pull/6/head
lkd9125 1 year ago
parent
commit
bfb27e137b
  1. 14
      pav-server/build.gradle
  2. 31
      pav-server/src/main/java/com/palnet/biz/api/acnt/terms/controller/AcntTermsController.java
  3. 15
      pav-server/src/main/java/com/palnet/biz/api/acnt/terms/model/AcntTermsRqModel.java
  4. 41
      pav-server/src/main/java/com/palnet/biz/api/acnt/terms/service/AcntTermsService.java
  5. 55
      pav-server/src/main/java/com/palnet/biz/api/bas/flight/controller/BasFlightController.java
  6. 26
      pav-server/src/main/java/com/palnet/biz/api/bas/flight/model/BasFlightPlanCreateRs.java
  7. 11
      pav-server/src/main/java/com/palnet/biz/api/bas/flight/model/BasFlightPlanLaancRs.java
  8. 5
      pav-server/src/main/java/com/palnet/biz/api/bas/flight/model/BasFlightPlanModel.java
  9. 76
      pav-server/src/main/java/com/palnet/biz/api/bas/flight/service/BasFlightService.java
  10. 109
      pav-server/src/main/java/com/palnet/biz/api/bas/flight/template/service/TemplateService.java
  11. 54
      pav-server/src/main/java/com/palnet/biz/api/bas/flight/template/vo/LaancPdfVO.java
  12. 16
      pav-server/src/main/java/com/palnet/biz/api/bas/flight/template/vo/PdfBaseVO.java
  13. 70
      pav-server/src/main/java/com/palnet/biz/jpa/entity/ComFileBas.java
  14. 53
      pav-server/src/main/java/com/palnet/biz/jpa/entity/PtyTermsAgreeTxn.java
  15. 12
      pav-server/src/main/java/com/palnet/biz/jpa/repository/com/ComFileBasRepository.java
  16. 1
      pav-server/src/main/java/com/palnet/biz/jpa/repository/com/ComIdntBasRepository.java
  17. 78
      pav-server/src/main/java/com/palnet/biz/jpa/repository/pty/PtyTermsQueryRepository.java
  18. 169
      pav-server/src/main/java/com/palnet/comn/utils/PdfUtils.java
  19. 34019
      pav-server/src/main/resources/air/airgeo.json
  20. 14
      pav-server/src/main/resources/application.yml
  21. 12
      pav-server/src/main/resources/static/css/pdf.css
  22. BIN
      pav-server/src/main/resources/static/font/NanumGothic.ttf
  23. 12
      pav-server/src/main/resources/templates/official_document.html

14
pav-server/build.gradle

@ -43,6 +43,15 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-cache'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
// thymeleaf
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
// pdf create
implementation 'com.itextpdf:itextpdf:5.5.13'
implementation 'com.itextpdf.tool:xmlworker:5.5.13'
implementation 'com.itextpdf:pdfa:7.2.3'
// db
runtimeOnly 'mysql:mysql-connector-java'
@ -83,18 +92,17 @@ dependencies {
// implementation 'com.bedatadriven:jackson-datatype-jts:2.4'
// implementation 'de.grundid.opendatalab:geojson-jackson:1.14'
// geometry
implementation 'com.esri.geometry:esri-geometry-api:2.2.4'
implementation 'org.locationtech.proj4j:proj4j:1.3.0'
implementation 'org.locationtech.proj4j:proj4j-epsg:1.3.0'
implementation 'org.locationtech.jts:jts-core:1.19.0'
implementation 'org.geotools:gt-geojson:29.2'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testAnnotationProcessor "org.mapstruct:mapstruct-processor:1.5.5.Final"
}
tasks.named('test') {

31
pav-server/src/main/java/com/palnet/biz/api/acnt/terms/controller/AcntTermsController.java

@ -1,37 +1,24 @@
package com.palnet.biz.api.acnt.terms.controller;
import java.util.List;
import com.palnet.biz.api.acnt.terms.model.AcntTermsRqModel;
import com.palnet.biz.api.acnt.terms.model.AcntTermsRsModel;
import com.palnet.biz.api.acnt.terms.service.AcntTermsService;
import com.palnet.biz.api.comn.response.BasicResponse;
import com.palnet.biz.api.comn.response.ErrorResponse;
import com.palnet.biz.api.comn.response.SuccessResponse;
import io.swagger.annotations.ApiOperation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.palnet.biz.api.acnt.terms.model.AcntTermsRqModel;
import com.palnet.biz.api.acnt.terms.model.AcntTermsRsModel;
import com.palnet.biz.api.acnt.terms.service.AcntTermsService;
import com.palnet.biz.api.anls.hstry.model.AnlsHstryDetailModel;
import com.palnet.biz.api.anls.hstry.model.AnlsHstryModel;
import com.palnet.biz.api.anls.hstry.model.AnlsHstryRqModel;
import com.palnet.biz.api.anls.hstry.service.AnlsHstryService;
import com.palnet.biz.api.comn.response.BasicResponse;
import com.palnet.biz.api.comn.response.ErrorResponse;
import com.palnet.biz.api.comn.response.SuccessResponse;
import com.palnet.biz.api.ctr.cntrl.service.CtrCntrlService;
import com.palnet.biz.sample.entity.SampleEntity;
import com.palnet.biz.sample.service.SampleService;
import com.palnet.comn.model.GPHistoryModel;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import java.util.List;
@Log4j2
@RestController

15
pav-server/src/main/java/com/palnet/biz/api/acnt/terms/model/AcntTermsRqModel.java

@ -1,11 +1,16 @@
package com.palnet.biz.api.acnt.terms.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
public class AcntTermsRqModel{
private String siteCd;
private String langDivCd;
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AcntTermsRqModel {
private String siteCd;
private String termsCtgryCd;
private String langDivCd;
}

41
pav-server/src/main/java/com/palnet/biz/api/acnt/terms/service/AcntTermsService.java

@ -1,35 +1,24 @@
package com.palnet.biz.api.acnt.terms.service;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.palnet.biz.api.acnt.terms.model.AcntTermsRqModel;
import com.palnet.biz.api.acnt.terms.model.AcntTermsRsModel;
import com.palnet.biz.jpa.repository.pty.PtyTermsQueryRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
@Slf4j
@RequiredArgsConstructor
@Service
public class AcntTermsService {
private Logger logger = LoggerFactory.getLogger(getClass());
// @Autowired
// private CtrCntrlBasRepository ctrCntrlBasRepository;
@Autowired
private PtyTermsQueryRepository query;
public List<AcntTermsRsModel> list(AcntTermsRqModel rq){
List<AcntTermsRsModel> resultList = query.list(rq);
return resultList;
}
private final PtyTermsQueryRepository query;
public List<AcntTermsRsModel> list(AcntTermsRqModel rq) {
return query.list(rq);
}
}

55
pav-server/src/main/java/com/palnet/biz/api/bas/flight/controller/BasFlightController.java

@ -25,13 +25,17 @@ import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import com.palnet.biz.api.bas.flight.service.BasFlightService;
import com.palnet.biz.api.bas.flight.template.service.TemplateService;
import com.palnet.biz.api.bas.flight.template.vo.LaancPdfVO;
import com.palnet.biz.api.comn.model.ComnPagingRs;
import com.palnet.biz.api.comn.response.BasicResponse;
import com.palnet.biz.api.comn.response.ErrorResponse;
import com.palnet.biz.api.comn.response.SuccessResponse;
import com.palnet.biz.jpa.entity.ComFileBas;
import com.palnet.biz.scheduler.ctr.service.CtrTrnsLctnService;
import com.palnet.comn.exception.CustomException;
import com.palnet.comn.utils.AreaUtils;
import com.palnet.comn.utils.PdfUtils;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
@ -48,13 +52,19 @@ public class BasFlightController {
private final Environment env;
private final CtrTrnsLctnService ctrTrnsLctnService;
private final AreaUtils utils;
private final TemplateService templateService;
private final PdfUtils pdfUtils;
@Autowired
public BasFlightController(BasFlightService basFlightService, Environment env, CtrTrnsLctnService ctrTrnsLctnService, AreaUtils AreaUtils) {
public BasFlightController(BasFlightService basFlightService, Environment env, CtrTrnsLctnService ctrTrnsLctnService, AreaUtils AreaUtils, TemplateService templateService, PdfUtils pdfUtils) {
this.basFlightService = basFlightService;
this.env = env;
this.ctrTrnsLctnService = ctrTrnsLctnService;
this.utils = AreaUtils;
this.templateService = templateService;
this.pdfUtils = pdfUtils;
}
@GetMapping("/area")
@ -117,12 +127,39 @@ public class BasFlightController {
return ResponseEntity.ok().body(new SuccessResponse<>(result));
}
// LAANC 비행계획서 검증
@PostMapping(value = "/plan/valid")
@ApiOperation(value = "비행계획서 등록")
@Tag(name = "비행계획서", description = "비행계획서 관련 API")
public ResponseEntity<? extends BasicResponse> validPlan(@RequestBody BasFlightPlanModel rq) {
BasFlightPlanLaancRs rs = null;
try {
log.debug(">>> rq : {}", rq);
rs = basFlightService.validPlan(rq);
log.debug(">>> rs : {}", rs);
} catch (CustomException e) {
Map<String, Object> resultMap = new HashMap<String, Object>();
log.error("IGNORE : {}", e);
resultMap.put("result", false);
resultMap.put("errorCode", e.getErrorCode());
resultMap.put("errorMessage", e.getMessage());
return ResponseEntity.ok().body(new SuccessResponse<Map>(resultMap));
} catch (Exception e) {
log.error("IGNORE : {}", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new ErrorResponse("Server Error", "-1"));
}
return ResponseEntity.ok().body(new SuccessResponse<>(rs));
}
// 비행계획서 등록
@PostMapping(value = "/plan/create")
@ApiOperation(value = "비행계획서 등록")
@Tag(name = "비행계획서", description = "비행계획서 관련 API")
public ResponseEntity<? extends BasicResponse> createPlan(@RequestBody BasFlightPlanModel rq) {
BasFlightPlanLaancRs rs = null;
BasFlightPlanCreateRs rs = null;
try {
log.debug(">>> rq : {}", rq);
rs = basFlightService.createPlan(rq);
@ -383,4 +420,18 @@ public class BasFlightController {
}
}
@PostMapping("/laanc-pdf/create")
@Tag(name = "비행계획서", description = "비행계획서 관련 API")
@ApiOperation(value = "Laanc 공문 PDF생성")
public void createLancePDF(@RequestBody LaancPdfVO vo) {
// PDF 생성 및 저장
ComFileBas comFileBas = templateService.makeLaancPdf(vo);
// DB 저장
templateService.save(comFileBas);
}
}

26
pav-server/src/main/java/com/palnet/biz/api/bas/flight/model/BasFlightPlanCreateRs.java

@ -0,0 +1,26 @@
package com.palnet.biz.api.bas.flight.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* packageName : com.palnet.biz.api.bas.flight.model
* fileName : BasFlightPlanErrorRS
* author : dhji
* date : 2023-09-19(019)
* description :
* ===========================================================
* DATE AUTHOR NOTE
* -----------------------------------------------------------
* 2023-09-19(019) dhji 최초 생성
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BasFlightPlanCreateRs {
private String pdfUrl;
}

11
pav-server/src/main/java/com/palnet/biz/api/bas/flight/model/BasFlightPlanLaancRs.java

@ -1,5 +1,6 @@
package com.palnet.biz.api.bas.flight.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@ -28,6 +29,8 @@ public class BasFlightPlanLaancRs {
private String evaluatedTargetAreaYn; // 평가대상지역여부 - 공역과 겹칠때만
private String flightAreaYn; // LAANC 기준 비행가능여부
private String corpRegYn; // 기업(사업자)가입여부
public boolean isValid() {
return "Y".equals(pilotQlfcYn)
&& "Y".equals(acrftInsuranceYn)
@ -36,4 +39,12 @@ public class BasFlightPlanLaancRs {
&& "Y".equals(evaluatedTargetAreaYn)
&& "Y".equals(flightAreaYn);
}
@JsonIgnore
public boolean isValidAirspace() {
return "N".equals(acrftDuplicatedYn)
&& "N".equals(planAreaDuplicatdYn)
&& "Y".equals(evaluatedTargetAreaYn)
&& "Y".equals(flightAreaYn);
}
}

5
pav-server/src/main/java/com/palnet/biz/api/bas/flight/model/BasFlightPlanModel.java

@ -1,5 +1,6 @@
package com.palnet.biz.api.bas.flight.model;
import com.palnet.biz.api.acnt.cstmr.model.AnctCstmrTermsModel;
import lombok.Data;
import java.time.Instant;
@ -34,4 +35,8 @@ public class BasFlightPlanModel {
// kac 추가 필드
private String corpRegYn;
private String serviceType;
// laanc 정보
private BasFlightPlanLaancRs validLance;
// 약관동의
private List<AnctCstmrTermsModel> terms;
}

76
pav-server/src/main/java/com/palnet/biz/api/bas/flight/service/BasFlightService.java

@ -1,5 +1,6 @@
package com.palnet.biz.api.bas.flight.service;
import com.palnet.biz.api.acnt.cstmr.model.AnctCstmrTermsModel;
import com.palnet.biz.api.acnt.jwt.model.JwtGroupModel;
import com.palnet.biz.api.acnt.jwt.utils.JwtTokenUtil;
import com.palnet.biz.api.bas.flight.model.*;
@ -11,13 +12,16 @@ import com.palnet.biz.api.external.service.TsService;
import com.palnet.biz.jpa.entity.*;
import com.palnet.biz.jpa.repository.flt.*;
import com.palnet.biz.jpa.repository.pty.PtyGroupQueryRepository;
import com.palnet.biz.jpa.repository.pty.PtyTermsAgreeTxnRepository;
import com.palnet.biz.scheduler.ctr.model.CtrTrnsLctnModel;
import com.palnet.biz.scheduler.ctr.service.CtrTrnsLctnService;
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.EncryptUtils;
import com.palnet.comn.utils.HttpUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.json.simple.JSONObject;
@ -25,7 +29,6 @@ import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
@ -40,6 +43,7 @@ import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@ -60,6 +64,7 @@ public class BasFlightService {
private final FltPlanPilotRepository fltPlanPilotRepository;
private final FltPlanQueryRepository fltPlanQueryRepository;
private final PtyGroupQueryRepository ptyPlanQueryRepository;
private final PtyTermsAgreeTxnRepository ptyTermsAgreeTxnRepository;
private final TsService tsService;
private final JwtTokenUtil jwtTokenUtil;
private final AreaUtils areaUtils;
@ -148,6 +153,7 @@ public class BasFlightService {
}
public BasFlightPlanLaancRs planValid(BasFlightPlanModel rq) {
BasFlightPlanLaancRs rs = BasFlightPlanLaancRs.builder()
.pilotQlfcYn("N")
.acrftInsuranceYn("N")
@ -177,7 +183,8 @@ public class BasFlightService {
}
if (effectivePlanList != null && !effectivePlanList.isEmpty()) {
plan: for (BasFlightPlanModel plan : effectivePlanList) {
plan:
for (BasFlightPlanModel plan : effectivePlanList) {
// 1. 구역 조회
List<FltPlanArea> areaList = fltPlanAreaRepository.findByPlanSnoOrderByPlanAreaSnoAsc(plan.getPlanSno());
@ -258,7 +265,8 @@ public class BasFlightService {
}
// 3. 중복 기체 확인
acrft: for (BasFlightPlanAreaCoordRq idntfNum : effectivePlanCount) {
acrft:
for (BasFlightPlanAreaCoordRq idntfNum : effectivePlanCount) {
for (BasFlightPlanArcrftModel rqArcrft : rq.getArcrftList()) {
if (rqArcrft.getIdntfNum().equals(idntfNum.getIdntfNum())) {
// throw new CustomException(ErrorCode.ARCRFT_DATA_DUPLICATE);
@ -294,15 +302,15 @@ public class BasFlightService {
AirspaceUtils airspaceUtils = AirspaceUtils.getInstance();
Geometry rqGeometry = airspaceUtils.createGeometryByCoordinate(rqCoordBufferList);
Integer fltElev = Integer.valueOf(rqArea.getFltElev());
if(fltElev == null) fltElev = 0;
if (fltElev == null) fltElev = 0;
AirspaceUtils.FeatureInfo featureInfo = new AirspaceUtils.FeatureInfo(null, null, 0, fltElev, rqGeometry);
// 평가 가능 지역 판단
boolean duplicatedAirspace = airspaceUtils.isDuplicatedAirspace(featureInfo);
rs.setEvaluatedTargetAreaYn(duplicatedAirspace ? "Y" : "N");
// 비행 가능 지역 판단
if(duplicatedAirspace){
if (duplicatedAirspace) {
boolean validLaancAirspace = airspaceUtils.isValidLaancAirspace(featureInfo);
rs.setFlightAreaYn(validLaancAirspace ? "Y" : "N");
} else {
@ -313,10 +321,8 @@ public class BasFlightService {
return rs;
}
// 비행계획서 등록
@Transactional
public BasFlightPlanLaancRs createPlan(BasFlightPlanModel rq) {
// 비행계획서 검증(LAANC)
public BasFlightPlanLaancRs validPlan(BasFlightPlanModel rq) {
// 비행계획서 유효성 검사.
BasFlightPlanLaancRs rs = this.planValid(rq);
@ -325,17 +331,30 @@ public class BasFlightService {
.pilotci("조종사CI")
.declarationnum("기체신고번호")
.build();
PilotValidRs pilotValidRs = tsService.callPilotValid(pilotValidRq);
if(pilotValidRs == null){
if (pilotValidRs == null) {
rs.setPilotQlfcYn("N");
rs.setAcrftInsuranceYn("N");
rs.setCorpRegYn("N");
} else {
rs.setPilotQlfcYn(pilotValidRs.getPilotcredentialyn());
rs.setAcrftInsuranceYn(pilotValidRs.getArcrftinsuranceyn());
rs.setCorpRegYn(pilotValidRs.getCorpregyn());
}
return rs;
}
// 비행계획서 등록
@Transactional
public BasFlightPlanCreateRs createPlan(BasFlightPlanModel rq) {
// 비행계획서 유효성 검사.
BasFlightPlanLaancRs basFlightPlanLaancRs = this.planValid(rq);
// LAANC가 검증된것들만 DB저장
if (!basFlightPlanLaancRs.isValidAirspace()) new CustomException(ErrorCode.PLAN_DATA_DUPLICATE);
// TODO LAANC가 검증된것들만 DB저장 - LAANC가 미승인이라도 저장해야하는지 확인 필요
if(!rs.isValid()) return rs;
// 비행계획서
String userId = jwtTokenUtil.getUserIdByToken();
@ -345,7 +364,7 @@ public class BasFlightService {
basEntity.setCreateUserId(userId);
basEntity.setUpdateUserId(userId);
// 사업자 유무 - TS 데이터
basEntity.setCorpRegYn(pilotValidRs.getCorpregyn()); // 사업자유무
basEntity.setCorpRegYn(rq.getValidLance().getCorpRegYn()); // 사업자유무
basEntity.setServiceType("PAV-KAC");
FltPlanBas rBasEntity = fltPlanBasRepository.save(basEntity);
@ -395,12 +414,32 @@ public class BasFlightService {
arcrftEntity.setCreateUserId(userId);
arcrftEntity.setUpdateUserId(userId);
// 추가 필드
arcrftEntity.setAcrftInsuranceYn(pilotValidRs.getArcrftinsuranceyn()); // 보험여부
arcrftEntity.setAcrftInsuranceYn(rq.getValidLance().getAcrftInsuranceYn()); // 보험여부
// arcrftEntity.setInsuranceExperiod(null); // 보헌 유효기간
// arcrftEntity.setCorporationNm(null); // 법인명
fltPlanArcrftRepository.save(arcrftEntity);
}
}
// 약관 저장
if (rq.getTerms() != null && !rq.getTerms().isEmpty()) {
for (AnctCstmrTermsModel agree : rq.getTerms()) {
PtyTermsAgreeTxn agreeEntity = new PtyTermsAgreeTxn();
agreeEntity.setCstmrSno(basEntity.getCstmrSno());
agreeEntity.setProcIp(HttpUtils.getRequestIp());
agreeEntity.setTermsSno(agree.getTermsSno());
agreeEntity.setUpdateDt(Instant.now());
agreeEntity.setUpdateUserId(userId);
agreeEntity.setAgreeYn(agree.getAgreeYn());
if (agree.getAgreeYn().equals("Y")) {
agreeEntity.setAgreeDt(Instant.now());
}
ptyTermsAgreeTxnRepository.save(agreeEntity);
}
} else {
throw new CustomException(ErrorCode.FAIL, "약관등록 실패");
}
// TODO PDF 생성 후 URL 가져오기
BasFlightPlanCreateRs rs = new BasFlightPlanCreateRs();
return rs;
}
@ -417,7 +456,7 @@ public class BasFlightService {
.declarationnum("기체신고번호")
.build();
PilotValidRs pilotValidRs = tsService.callPilotValid(pilotValidRq);
if(pilotValidRs == null){
if (pilotValidRs == null) {
rs.setPilotQlfcYn("N");
rs.setAcrftInsuranceYn("N");
} else {
@ -426,7 +465,7 @@ public class BasFlightService {
}
// LAANC가 검증된것들만 DB저장
if(!rs.isValid()) return rs;
if (!rs.isValid()) return rs;
if (rq != null && rq.getPlanSno() != null) {
String userId = jwtTokenUtil.getUserIdByToken();
@ -799,4 +838,5 @@ public class BasFlightService {
return jsonObject;
}
}

109
pav-server/src/main/java/com/palnet/biz/api/bas/flight/template/service/TemplateService.java

@ -0,0 +1,109 @@
package com.palnet.biz.api.bas.flight.template.service;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.Instant;
import java.util.Map;
import java.util.Map.Entry;
import javax.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.palnet.biz.api.acnt.jwt.utils.JwtTokenUtil;
import com.palnet.biz.api.bas.flight.template.vo.LaancPdfVO;
import com.palnet.biz.jpa.entity.ComFileBas;
import com.palnet.biz.jpa.repository.com.ComFileBasRepository;
import com.palnet.comn.utils.InstantUtils;
import com.palnet.comn.utils.PdfUtils;
import lombok.extern.slf4j.Slf4j;
@Service
@Slf4j
public class TemplateService {
@Autowired
private PdfUtils pdfUtils;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private ComFileBasRepository comFileBasRepository;
@Value("${base-url}")
private String BASE_PATH;
private final String FILE_EXTENSION = ".pdf";
/**
* 파일명 만들기, 앞에 기본으로 금일 날짜있음
* [예시] 20230102_[추가파라미터]
* @param etcName
* @return
*/
private String getLaancSaveName(String ...etcName){
String date = InstantUtils.toDateString(Instant.now()).replace("-", "");
StringBuilder result = new StringBuilder();
result.append(date);
for(String name : etcName){
result.append("-")
.append(name);
}
result.append(this.FILE_EXTENSION);
return result.toString();
}
/**
* PDF 생성
* @param vo
* @return
*/
public ComFileBas makeLaancPdf(LaancPdfVO vo){
String airUser = vo.getUserName();
String etc = String.valueOf(System.currentTimeMillis()); // 동명이인 방지
String fileName = getLaancSaveName(airUser, etc);
String htmlContent = pdfUtils.getHtmlToString(vo);
ComFileBas comFileBas = pdfUtils.generatePDF(htmlContent, fileName);
String userId = jwtTokenUtil.getUserIdByToken();
comFileBas.setCreateUserId(userId);
return comFileBas;
}
/**
* DB 인서트, 최신데이터 가져온 Group NO 1추가함
* @param comFileBas
*/
@Transactional
public void save(ComFileBas comFileBas){
// DB Insert
ComFileBas lastComFileBas = comFileBasRepository.findFirstByOrderByFileSnoDesc();
int fileGroupNo = (lastComFileBas == null) ? 1 : lastComFileBas.getFileGroupNo() + 1;
comFileBas.setFileGroupNo(fileGroupNo);
comFileBasRepository.save(comFileBas);
}
}

54
pav-server/src/main/java/com/palnet/biz/api/bas/flight/template/vo/LaancPdfVO.java

@ -0,0 +1,54 @@
package com.palnet.biz.api.bas.flight.template.vo;
import java.lang.reflect.Field;
import java.util.HashMap;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
public class LaancPdfVO extends PdfBaseVO{
// TODO : 아직 파라미터가 정해지지 않음
private String userName; // 조종사 이름
private int userAge; // 조종사 나이
private String userGender; // 조종사 성별
public LaancPdfVO (){
init();
}
public HashMap<String, Object> getParam(){
Field[] fields = getClass().getDeclaredFields();
HashMap<String, Object> result = new HashMap<>();
try {
for(Field field : fields){
Object value = field.get(this);
String key = field.getName();
result.put(key, value);
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e){
e.printStackTrace();
}
return result;
}
@Override
public void init() {
super.setTemplate("official_document");
}
}

16
pav-server/src/main/java/com/palnet/biz/api/bas/flight/template/vo/PdfBaseVO.java

@ -0,0 +1,16 @@
package com.palnet.biz.api.bas.flight.template.vo;
import java.util.Map;
import lombok.Data;
@Data
public abstract class PdfBaseVO {
private String template;
public abstract void init();
public abstract Map<String, Object> getParam();
}

70
pav-server/src/main/java/com/palnet/biz/jpa/entity/ComFileBas.java

@ -0,0 +1,70 @@
package com.palnet.biz.jpa.entity;
import lombok.Data;
import javax.persistence.*;
import java.io.Serializable;
import java.time.Instant;
/**
* The persistent class for the COM_IDNTF_BAS database table.
*
*/
@Data
@Entity
@Table(name="COM_FILE_BAS")
@NamedQuery(name="ComFileBas.findAll", query="SELECT c FROM ComFileBas c")
public class ComFileBas implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name="FILE_SNO")
private int fileSno;
@Column(name="FILE_GROUP_NO")
private int fileGroupNo;
@Column(name="FILE_SAVE_NM")
private String fileSaveNm;
@Column(name="FILE_ORI_NM")
private String fileOriNm;
@Column(name = "FILE_PATH")
private String filePath;
@Column(name="FILE_EXT")
private String fileExt;
// @Temporal(TemporalType.TIMESTAMP)
@Column(name="FILE_SIZE")
private String fileSize;
// @Temporal(TemporalType.TIMESTAMP)
@Column(name="DEL_YN", columnDefinition = "TIMESTAMP")
private String delYn = "N";
@Column(name="DEL_USER_ID")
private String delUserId;
@Column(name="DEL_DT")
private Instant delDt;
@Column(name="CREATE_USER_ID")
private String createUserId;
@Column(name="CREATE_DT" , columnDefinition = "TIMESTAMP")
private Instant createDt;
}

53
pav-server/src/main/java/com/palnet/biz/jpa/entity/PtyTermsAgreeTxn.java

@ -1,6 +1,9 @@
package com.palnet.biz.jpa.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.io.Serializable;
@ -9,41 +12,43 @@ import java.time.Instant;
/**
* The persistent class for the PTY_TERMS_AGREE_TXN database table.
*
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name="PTY_TERMS_AGREE_TXN")
@NamedQuery(name="PtyTermsAgreeTxn.findAll", query="SELECT p FROM PtyTermsAgreeTxn p")
@Table(name = "PTY_TERMS_AGREE_TXN")
@NamedQuery(name = "PtyTermsAgreeTxn.findAll", query = "SELECT p FROM PtyTermsAgreeTxn p")
public class PtyTermsAgreeTxn implements Serializable {
private static final long serialVersionUID = 1L;
private static final long serialVersionUID = 1L;
@Id
@Column(name="AGREE_SNO")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int agreeSno;
@Id
@Column(name = "AGREE_SNO")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int agreeSno;
// @Temporal(TemporalType.TIMESTAMP)
@Column(name="AGREE_DT", columnDefinition = "TIMESTAMP")
private Instant agreeDt;
// @Temporal(TemporalType.TIMESTAMP)
@Column(name = "AGREE_DT", columnDefinition = "TIMESTAMP")
private Instant agreeDt;
@Column(name="AGREE_YN")
private String agreeYn;
@Column(name = "AGREE_YN")
private String agreeYn;
@Column(name="CSTMR_SNO")
private int cstmrSno;
@Column(name = "CSTMR_SNO")
private int cstmrSno;
@Column(name="PROC_IP")
private String procIp;
@Column(name = "PROC_IP")
private String procIp;
@Column(name="TERMS_SNO")
private int termsSno;
@Column(name = "TERMS_SNO")
private int termsSno;
// @Temporal(TemporalType.TIMESTAMP)
@Column(name="UPDATE_DT", columnDefinition = "TIMESTAMP")
private Instant updateDt;
// @Temporal(TemporalType.TIMESTAMP)
@Column(name = "UPDATE_DT", columnDefinition = "TIMESTAMP")
private Instant updateDt;
@Column(name="UPDATE_USER_ID")
private String updateUserId;
@Column(name = "UPDATE_USER_ID")
private String updateUserId;
}

12
pav-server/src/main/java/com/palnet/biz/jpa/repository/com/ComFileBasRepository.java

@ -0,0 +1,12 @@
package com.palnet.biz.jpa.repository.com;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.palnet.biz.jpa.entity.ComFileBas;
@Repository
public interface ComFileBasRepository extends JpaRepository<ComFileBas, String>{
public ComFileBas findFirstByOrderByFileSnoDesc();
}

1
pav-server/src/main/java/com/palnet/biz/jpa/repository/com/ComIdntBasRepository.java

@ -1,7 +1,6 @@
package com.palnet.biz.jpa.repository.com;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;

78
pav-server/src/main/java/com/palnet/biz/jpa/repository/pty/PtyTermsQueryRepository.java

@ -18,44 +18,42 @@ import lombok.extern.log4j.Log4j2;
@Log4j2
@Repository
@RequiredArgsConstructor
public class PtyTermsQueryRepository{
private final JPAQueryFactory query;
public List<AcntTermsRsModel> list(AcntTermsRqModel rq){
QPtyTermsBas basEntity = QPtyTermsBas.ptyTermsBas;
QPtyTermsDtl dtlEntity = QPtyTermsDtl.ptyTermsDtl;
BooleanBuilder builder = new BooleanBuilder();
builder.and(basEntity.lastTxnYn.eq("Y"));
builder.and(basEntity.siteCd.eq(rq.getSiteCd()));
List<AcntTermsRsModel> queryList = query.select(Projections.bean(AcntTermsRsModel.class ,
basEntity.termsSno,
basEntity.siteCd,
basEntity.estbshDate,
basEntity.termsCtgryCd,
dtlEntity.id.langDivCd,
dtlEntity.termsTitleNm,
dtlEntity.termsCn,
dtlEntity.simpleCn
))
.from(basEntity)
.leftJoin(dtlEntity)
.on(basEntity.termsSno.eq(dtlEntity.id.termsSno).and(dtlEntity.id.langDivCd.eq(rq.getLangDivCd())))
.where(builder)
.orderBy(basEntity.updateDt.desc())
.fetch();
return queryList;
}
public class PtyTermsQueryRepository {
private final JPAQueryFactory query;
public List<AcntTermsRsModel> list(AcntTermsRqModel rq) {
QPtyTermsBas basEntity = QPtyTermsBas.ptyTermsBas;
QPtyTermsDtl dtlEntity = QPtyTermsDtl.ptyTermsDtl;
BooleanBuilder builder = new BooleanBuilder();
builder.and(basEntity.lastTxnYn.eq("Y"));
builder.and(basEntity.siteCd.eq(rq.getSiteCd()));
builder.and(basEntity.termsCtgryCd.eq(rq.getTermsCtgryCd()));
List<AcntTermsRsModel> queryList = query.select(Projections.bean(AcntTermsRsModel.class,
basEntity.termsSno,
basEntity.siteCd,
basEntity.estbshDate,
basEntity.termsCtgryCd,
dtlEntity.id.langDivCd,
dtlEntity.termsTitleNm,
dtlEntity.termsCn,
dtlEntity.simpleCn
))
.from(basEntity)
.leftJoin(dtlEntity)
.on(basEntity.termsSno.eq(dtlEntity.id.termsSno).and(dtlEntity.id.langDivCd.eq(rq.getLangDivCd())))
.where(builder)
.orderBy(basEntity.updateDt.desc())
.fetch();
return queryList;
}
}

169
pav-server/src/main/java/com/palnet/comn/utils/PdfUtils.java

@ -0,0 +1,169 @@
package com.palnet.comn.utils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.Instant;
import java.util.Map;
import java.util.Map.Entry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.tool.xml.XMLWorker;
import com.itextpdf.tool.xml.XMLWorkerFontProvider;
import com.itextpdf.tool.xml.XMLWorkerHelper;
import com.itextpdf.tool.xml.css.CssFile;
import com.itextpdf.tool.xml.css.StyleAttrCSSResolver;
import com.itextpdf.tool.xml.html.CssAppliers;
import com.itextpdf.tool.xml.html.CssAppliersImpl;
import com.itextpdf.tool.xml.html.Tags;
import com.itextpdf.tool.xml.parser.XMLParser;
import com.itextpdf.tool.xml.pipeline.css.CSSResolver;
import com.itextpdf.tool.xml.pipeline.css.CssResolverPipeline;
import com.itextpdf.tool.xml.pipeline.end.PdfWriterPipeline;
import com.itextpdf.tool.xml.pipeline.html.HtmlPipeline;
import com.itextpdf.tool.xml.pipeline.html.HtmlPipelineContext;
import com.palnet.biz.api.acnt.jwt.utils.JwtTokenUtil;
import com.palnet.biz.api.bas.flight.template.vo.PdfBaseVO;
import com.palnet.biz.jpa.entity.ComFileBas;
@Component
public class PdfUtils {
@Autowired
private TemplateEngine templateEngine;
@Value("${base-url}")
private String BASE_PATH;
private final String FILE_EXTENSION = ".pdf";
/**
* Thymeleaf HTML 파일을 데이터 바인딩하여 String으로 변환
* @param <T>
* @param pdfDto
* @return
*/
public <T extends PdfBaseVO> String getHtmlToString(T pdfDto){
Map<String, Object> param = pdfDto.getParam();
// Thymeleaf 방식 html에 입힐 데이터 바인딩
Context context = new Context();
for(Entry<String, Object> entry : param.entrySet()){
String key = entry.getKey();
context.setVariable(key, param.get(key));
}
// 앞 뒤 prefix, suffix는 yml에 정의해놓음
// html에 바인딩할 데이터 넣고 파싱이후 String형식으로 뽑아옴
String htmlContent = templateEngine.process(pdfDto.getTemplate(), context);
return htmlContent;
}
/**
* HTML 태그로 이루어진 String값을 PDF로 변환
* @param htmlContent
* @param fileName
* @return
*/
public ComFileBas generatePDF(String htmlContent, String fileName){
// PDF 용지 설정하기
Document pdfDocument = new Document();
ComFileBas result = new ComFileBas();
String pdfPath = new StringBuilder()
.append(this.BASE_PATH)
.append(InstantUtils.toDateString(Instant.now()).replace("-", ""))
.append("/").toString();
try {
// 폴더 생성
new File(pdfPath).mkdirs();
// File 저장 기본경로는 main 아래로 기본으로 잡힘
PdfWriter writer = PdfWriter.getInstance(pdfDocument, new FileOutputStream(pdfPath + fileName));
writer.setInitialLeading(12.5f);
pdfDocument.open();
/*
CSS 설정
TODO : CSS 파일이 따로 있을시 임포트해주는 방법, CSS 파일 적용은 필수는 아니지만, 글꼴 적용은 꼭해야만
jar변환 CSS파일을 찾을수도 있어서 InputStream 사용
CSS의 색깔은 키워드로 표기 X, #e73a3a 16진법으로 표기 해야함
*/
InputStream cssStream = getClass().getClassLoader().getResourceAsStream("static/css/pdf.css");
CssFile cssFile = XMLWorkerHelper.getCSS(cssStream);
CSSResolver cssResolver = new StyleAttrCSSResolver();
cssResolver.addCss(cssFile);
// Font 설정
XMLWorkerFontProvider fontProvider = new XMLWorkerFontProvider(XMLWorkerFontProvider.DONTLOOKFORFONTS);
fontProvider.register("static/font/NanumGothic.ttf", "NanumGothic");
CssAppliers cssAppliers = new CssAppliersImpl(fontProvider);
// XML Worker에 넣을 PipeLine객체 생성, CssResolverPipeline 이 객체를 최종적으로 넣어야 Font,Css가 적용됨
HtmlPipelineContext htmlPipelineContext = new HtmlPipelineContext(cssAppliers);
htmlPipelineContext.setTagFactory(Tags.getHtmlTagProcessorFactory());
PdfWriterPipeline pdfWriterPipeline = new PdfWriterPipeline(pdfDocument, writer);
HtmlPipeline htmlPipeline = new HtmlPipeline(htmlPipelineContext, pdfWriterPipeline);
CssResolverPipeline cssResolverPipeline = new CssResolverPipeline(cssResolver, htmlPipeline);
StringReader stringReader = new StringReader(htmlContent);
XMLWorker xmlWorker = new XMLWorker(cssResolverPipeline, true);
XMLParser xmlParser = new XMLParser(xmlWorker, Charset.forName("UTF-8"));
xmlParser.parse(stringReader);
} catch (IOException e) {
e.printStackTrace();
} catch (DocumentException e){
e.printStackTrace();
} catch (Exception e){
e.printStackTrace();
}
finally{
pdfDocument.close();
}
result.setFileGroupNo(0);
result.setFilePath(pdfPath);
result.setFileExt(this.FILE_EXTENSION);
result.setCreateDt(Instant.now());
result.setFileSaveNm(fileName);
result.setFileOriNm(fileName);
try {
String filePath = result.getFileOriNm();
long fileSize = Files.size(Paths.get(pdfPath + filePath)) / 1024;
if(fileSize >= 0) result.setFileSize(String.valueOf(fileSize));
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
}

34019
pav-server/src/main/resources/air/airgeo.json

File diff suppressed because it is too large Load Diff

14
pav-server/src/main/resources/application.yml

@ -30,6 +30,12 @@ spring:
ddl-auto: none
properties:
hibernate:
thymeleaf:
mode: HTML5
cache: false
encoding: UTF-8
prefix: classpath:/templates/
suffix: .html
server:
port: 8080
@ -57,6 +63,8 @@ api:
client-secret-key: Q4K4OtUYol
search-url : https://openapi.naver.com/v1/search/local.json
base-url:
---
spring:
@ -102,6 +110,7 @@ api:
client-secret-key: Q4K4OtUYol
search-url : https://openapi.naver.com/v1/search/local.json
base-url: /data/server/files
---
spring:
@ -156,6 +165,7 @@ api:
client-secret-key: Q4K4OtUYol
search-url : https://openapi.naver.com/v1/search/local.json
base-url: /data/server/files
---
spring:
@ -204,4 +214,6 @@ api:
naver:
client-id: WGEct3bJhQC0pyMsP_GK
client-secret-key: Q4K4OtUYol
search-url : https://openapi.naver.com/v1/search/local.json
search-url : https://openapi.naver.com/v1/search/local.json
base-url: /data/server/files

12
pav-server/src/main/resources/static/css/pdf.css

@ -0,0 +1,12 @@
body{
font-family: NanumGothic;
}
p{
background-color: #e73a3a;
}
div {
background-color: #e73a3a;
}

BIN
pav-server/src/main/resources/static/font/NanumGothic.ttf

Binary file not shown.

12
pav-server/src/main/resources/templates/official_document.html

@ -0,0 +1,12 @@
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
<p>Thymeleaf Tutorials</p>
<p>이름 : [[${userName}]]</p>
<p>나이 : [[${userAge}]]</p>
<p>성별 : [[${userGender}]]</p>
</body>
</html>
Loading…
Cancel
Save