Browse Source

Merge pull request 'feature/cns/faq-qna' (#9) from feature/cns/faq-qna into develop

Reviewed-on: pav/pav-be-kac#9
pull/10/head
lkd9125 11 months ago
parent
commit
6229cb4af9
  1. 74
      pav-server/src/main/java/com/palnet/biz/api/cns/faq/controller/CnsFaqController.java
  2. 32
      pav-server/src/main/java/com/palnet/biz/api/cns/faq/model/FaqListModel.java
  3. 12
      pav-server/src/main/java/com/palnet/biz/api/cns/faq/model/FaqListRQModel.java
  4. 105
      pav-server/src/main/java/com/palnet/biz/api/cns/faq/service/CnsFaqService.java
  5. 102
      pav-server/src/main/java/com/palnet/biz/api/cns/qna/controller/CnsQnaController.java
  6. 36
      pav-server/src/main/java/com/palnet/biz/api/cns/qna/model/QnaBasModel.java
  7. 34
      pav-server/src/main/java/com/palnet/biz/api/cns/qna/model/QnaDetailRSModel.java
  8. 23
      pav-server/src/main/java/com/palnet/biz/api/cns/qna/model/QnaInsertRQModel.java
  9. 33
      pav-server/src/main/java/com/palnet/biz/api/cns/qna/model/QnaRSModel.java
  10. 11
      pav-server/src/main/java/com/palnet/biz/api/cns/qna/model/QnaSelectListRQ.java
  11. 216
      pav-server/src/main/java/com/palnet/biz/api/cns/qna/service/CnsQnaService.java
  12. 39
      pav-server/src/main/java/com/palnet/biz/api/comn/file/constant/ExtensionConstant.java
  13. 257
      pav-server/src/main/java/com/palnet/biz/api/comn/file/service/ComnFileService.java
  14. 53
      pav-server/src/main/java/com/palnet/biz/jpa/entity/CnsFaqBas.java
  15. 60
      pav-server/src/main/java/com/palnet/biz/jpa/entity/CnsQnaBas.java
  16. 27
      pav-server/src/main/java/com/palnet/biz/jpa/repository/cns/CnsFaqBasRepository.java
  17. 111
      pav-server/src/main/java/com/palnet/biz/jpa/repository/cns/CnsFaqQueryRepository.java
  18. 23
      pav-server/src/main/java/com/palnet/biz/jpa/repository/cns/CnsQnaBasRepository.java
  19. 69
      pav-server/src/main/java/com/palnet/biz/jpa/repository/cns/CnsQnaQueryRepository.java
  20. 7
      pav-server/src/main/java/com/palnet/biz/jpa/repository/com/ComFileBasRepository.java
  21. 105
      pav-server/src/main/java/com/palnet/comn/utils/PdfUtils.java

74
pav-server/src/main/java/com/palnet/biz/api/cns/faq/controller/CnsFaqController.java

@ -0,0 +1,74 @@
package com.palnet.biz.api.cns.faq.controller;
import java.util.List;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
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.cns.faq.model.FaqListRQModel;
import com.palnet.biz.api.cns.faq.model.FaqListModel;
import com.palnet.biz.api.cns.faq.service.CnsFaqService;
import com.palnet.biz.api.comn.response.BasicResponse;
import com.palnet.biz.api.comn.response.SuccessResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@RestController
@Slf4j
@RequiredArgsConstructor
@RequestMapping(value = "/api/bas/cns/faq")
public class CnsFaqController {
private final CnsFaqService service;
/*FAQ 목록 조회*/
@PostMapping("/list")
public ResponseEntity<? extends BasicResponse> getFaqList(@RequestBody FaqListRQModel model) {
List<FaqListModel> result = null;
result = service.getFaqList(model);
return ResponseEntity.ok().body(new SuccessResponse<>(result));
}
/*FAQ 상세 조회*/
@GetMapping("/detail")
public ResponseEntity<FaqListModel> getFaqDetail(int sno) {
FaqListModel result = null;
result = service.getFaqDetail(sno);
return ResponseEntity.ok().body(result);
}
/*FAQ 작성*/
@PostMapping("/insert")
public void insertFaq(@RequestBody FaqListModel model) {
service.insertFaq(model);
}
/*FAQ 업데이트*/
@PostMapping("/update")
public void updateFaq(@RequestBody FaqListModel model) {
service.updateFaq(model);
}
/*FAQ 논리 삭제*/
@PostMapping("/delete")
public void deleteFaq(int sno) {
service.deleteFaq(sno);
}
}

32
pav-server/src/main/java/com/palnet/biz/api/cns/faq/model/FaqListModel.java

@ -0,0 +1,32 @@
package com.palnet.biz.api.cns.faq.model;
import java.time.Instant;
import lombok.Data;
@Data
public class FaqListModel {
private int faqSno;
private String category;
private String title;
private String content;
private int viewCnt;
private String expsrYn;
private String delYn;
private String createUserId;
private Instant createDt;
private String updateUserId;
private Instant updateDt;
}

12
pav-server/src/main/java/com/palnet/biz/api/cns/faq/model/FaqListRQModel.java

@ -0,0 +1,12 @@
package com.palnet.biz.api.cns.faq.model;
import lombok.Data;
@Data
public class FaqListRQModel {
private String category;
private String word;
}

105
pav-server/src/main/java/com/palnet/biz/api/cns/faq/service/CnsFaqService.java

@ -0,0 +1,105 @@
package com.palnet.biz.api.cns.faq.service;
import java.time.Instant;
import java.util.List;
import javax.transaction.Transactional;
import org.springframework.stereotype.Service;
import com.palnet.biz.api.cns.faq.model.FaqListModel;
import com.palnet.biz.api.cns.faq.model.FaqListRQModel;
import com.palnet.biz.jpa.entity.CnsFaqBas;
import com.palnet.biz.jpa.repository.cns.CnsFaqBasRepository;
import com.palnet.biz.jpa.repository.cns.CnsFaqQueryRepository;
import com.palnet.comn.code.ErrorCode;
import com.palnet.comn.exception.CustomException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
@RequiredArgsConstructor
public class CnsFaqService {
private final CnsFaqQueryRepository query;
private final CnsFaqBasRepository cnsFaqBasRepository;
public List<FaqListModel> getFaqList(FaqListRQModel model){
List<FaqListModel> result = query.getFaqList(model.getCategory(), model.getWord());
if(result == null || result.size() < 1) throw new CustomException(ErrorCode.DATA_NO);
return result;
}
public FaqListModel getFaqDetail(int sno){
cnsFaqBasRepository.updateViewCnt(sno);
FaqListModel result = query.getFaqDetail(sno);
if(result == null) throw new CustomException(ErrorCode.DATA_NO);
return result;
}
@Transactional
public void insertFaq(FaqListModel model){
CnsFaqBas bas = new CnsFaqBas();
bas.setCreateDt(Instant.now());
bas.setUpdateDt(Instant.now());
bas.setCategory(model.getCategory());
bas.setContent(model.getContent());
bas.setCreateUserId(model.getCreateUserId());
bas.setDelYn(model.getDelYn());
bas.setExpsrYn(model.getExpsrYn());
bas.setTitle(model.getTitle());
bas.setUpdateUserId(model.getUpdateUserId());
bas.setViewCnt(0);
try {
cnsFaqBasRepository.save(bas);
} catch(Exception e) {
throw new CustomException(ErrorCode.NON_VALID_PARAMETER);
}
}
@Transactional
public void updateFaq(FaqListModel model) {
FaqListModel prevData = query.getFaqDetail(model.getFaqSno());
if(prevData == null) throw new CustomException(ErrorCode.DATA_NO);
if(model.getCategory() != null) prevData.setCategory(model.getCategory());
if(model.getTitle() != null) prevData.setTitle(model.getTitle());
if(model.getContent() != null) prevData.setContent(model.getContent());
if(model.getExpsrYn() != null) prevData.setExpsrYn(model.getExpsrYn());
if(model.getUpdateUserId() != null) prevData.setUpdateUserId(model.getUpdateUserId());
prevData.setUpdateDt(Instant.now());
try {
query.updateFaq(prevData);
} catch(Exception e) {
throw new CustomException(ErrorCode.NON_VALID_PARAMETER);
}
}
public void deleteFaq(int sno) {
int delCnt = cnsFaqBasRepository.deleteFaq(sno);
if(delCnt == 0) throw new CustomException(ErrorCode.DATA_NO);
}
}

102
pav-server/src/main/java/com/palnet/biz/api/cns/qna/controller/CnsQnaController.java

@ -0,0 +1,102 @@
package com.palnet.biz.api.cns.qna.controller;
import java.util.List;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.palnet.biz.api.cns.qna.model.QnaBasModel;
import com.palnet.biz.api.cns.qna.model.QnaDetailRSModel;
import com.palnet.biz.api.cns.qna.model.QnaInsertRQModel;
import com.palnet.biz.api.cns.qna.model.QnaRSModel;
import com.palnet.biz.api.cns.qna.model.QnaSelectListRQ;
import com.palnet.biz.api.cns.qna.service.CnsQnaService;
import io.swagger.annotations.ApiOperation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@RestController
@Slf4j
@RequiredArgsConstructor
@RequestMapping("/api/cns/qna")
public class CnsQnaController {
private final CnsQnaService cnsQnaService;
@PostMapping(value = "/insert", consumes = "multipart/form-data")
@Tag(name = "QNA", description = "QNA 관련 API")
@ApiOperation(value = "QnA 등록[답변추가]")
public ResponseEntity<Boolean> insertQna(QnaInsertRQModel rq){
boolean result = cnsQnaService.insertQna(rq);
return ResponseEntity.ok().body(result);
}
@GetMapping("/select")
@Tag(name = "QNA", description = "QNA 관련 API")
@ApiOperation(value = "QnA 리스트 조회")
public ResponseEntity<List<QnaBasModel>> selectQna(QnaSelectListRQ rq){
List<QnaBasModel> result = cnsQnaService.selectQnaList(rq);
return ResponseEntity.ok().body(result);
}
@GetMapping("/{qnaSno}/detail")
@Tag(name = "QNA", description = "QNA 관련 API")
@ApiOperation(value = "QnA 상세보기")
public ResponseEntity<List<QnaDetailRSModel>> detailQna(@PathVariable int qnaSno){
List<QnaDetailRSModel> reulst = cnsQnaService.getQnaDetail(qnaSno);
return ResponseEntity.ok().body(reulst);
}
@PostMapping("/{qnaSno}/update")
@Tag(name = "QNA", description = "QNA 관련 API")
@ApiOperation(value = "QnA 업데이트")
public ResponseEntity<Boolean> updateQna(@PathVariable int qnaSno, QnaInsertRQModel rq){
boolean result = cnsQnaService.updateQna(qnaSno, rq);
return ResponseEntity.ok().body(result);
}
@GetMapping("/{qnaSno}/delete")
@Tag(name = "QNA", description = "QNA 관련 API")
@ApiOperation(value = "QnA 삭제하기")
public ResponseEntity<Boolean> deleteQna(@PathVariable int qnaSno){
boolean result = cnsQnaService.deleteQna(qnaSno);
return ResponseEntity.ok().body(result);
}
/**
* Qna Files 개별삭제 TODO:: 파일 업데이트의 기획이 어떻게 되는지 모름
* @param qnaSno
* @param fileSno
* @return
*/
@GetMapping("/{qnaSno}/{fileSno}/delete")
@Tag(name = "QNA", description = "QNA 관련 API")
@ApiOperation(value = "QnA 파일 개별삭제")
public ResponseEntity<Boolean> deleteQnaFile(@PathVariable int qnaSno, @PathVariable int fileSno){
log.warn("CnsQnaController - deleteQnaFile()");
boolean result = cnsQnaService.deleteQnaFile(fileSno);
return ResponseEntity.ok().body(result);
}
}

36
pav-server/src/main/java/com/palnet/biz/api/cns/qna/model/QnaBasModel.java

@ -0,0 +1,36 @@
package com.palnet.biz.api.cns.qna.model;
import java.time.Instant;
import lombok.Data;
@Data
public class QnaBasModel {
private int qnaSno;
private int targetSno;
private String category;
private String title;
private String content;
private int fileGroupNo;
private String expsrYn;
private String delYn;
private int viewCnt;
private String createUserId;
private Instant createDt;
private String updateUserId;
private Instant updateDt;
}

34
pav-server/src/main/java/com/palnet/biz/api/cns/qna/model/QnaDetailRSModel.java

@ -0,0 +1,34 @@
package com.palnet.biz.api.cns.qna.model;
import java.time.Instant;
import java.util.List;
import com.palnet.biz.jpa.entity.ComFileBas;
import lombok.Data;
@Data
public class QnaDetailRSModel {
private int qnaSno;
private int targetSno;
private String category;
private String title;
private String content;
private Integer viewCnt;
private String createUserId;
private Instant createDt;
private String updateUserId;
private Instant updateDt;
private List<ComFileBas> files;
}

23
pav-server/src/main/java/com/palnet/biz/api/cns/qna/model/QnaInsertRQModel.java

@ -0,0 +1,23 @@
package com.palnet.biz.api.cns.qna.model;
import java.util.List;
import org.springframework.web.multipart.MultipartFile;
import lombok.Data;
@Data
public class QnaInsertRQModel {
private Integer qnaSno;
private Integer targetSno; // 상위 Qna 번호
private String category;
private String title;
private String content;
private List<MultipartFile> files;
}

33
pav-server/src/main/java/com/palnet/biz/api/cns/qna/model/QnaRSModel.java

@ -0,0 +1,33 @@
package com.palnet.biz.api.cns.qna.model;
import lombok.Data;
@Data
public class QnaRSModel{
private int code;
private String desc;
private Object body;
public QnaRSModel() {
this.code = 200;
}
public QnaRSModel(Object body){
this.code = 200;
this.body = body;
}
public QnaRSModel(int code, Object body){
this.code = code;
this.body = body;
}
public QnaRSModel(int code, String desc, Object body){
this.code = code;
this.desc = desc;
this.body = body;
}
}

11
pav-server/src/main/java/com/palnet/biz/api/cns/qna/model/QnaSelectListRQ.java

@ -0,0 +1,11 @@
package com.palnet.biz.api.cns.qna.model;
import lombok.Data;
@Data
public class QnaSelectListRQ {
private String word;
private String category;
}

216
pav-server/src/main/java/com/palnet/biz/api/cns/qna/service/CnsQnaService.java

@ -0,0 +1,216 @@
package com.palnet.biz.api.cns.qna.service;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import javax.transaction.Transactional;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import com.palnet.biz.api.acnt.jwt.utils.JwtTokenUtil;
import com.palnet.biz.api.cns.qna.model.QnaBasModel;
import com.palnet.biz.api.cns.qna.model.QnaDetailRSModel;
import com.palnet.biz.api.cns.qna.model.QnaInsertRQModel;
import com.palnet.biz.api.cns.qna.model.QnaSelectListRQ;
import com.palnet.biz.api.comn.file.service.ComnFileService;
import com.palnet.biz.jpa.entity.CnsQnaBas;
import com.palnet.biz.jpa.entity.ComFileBas;
import com.palnet.biz.jpa.repository.cns.CnsQnaBasRepository;
import com.palnet.biz.jpa.repository.cns.CnsQnaQueryRepository;
import com.palnet.biz.jpa.repository.com.ComFileBasRepository;
import com.palnet.comn.code.ErrorCode;
import com.palnet.comn.exception.CustomException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Service
@Slf4j
@RequiredArgsConstructor
public class CnsQnaService {
private final CnsQnaBasRepository cnsQnaBasRepository;
private final CnsQnaQueryRepository cnsQnaQueryRepository;
private final ComFileBasRepository comFileBasRepository;
private final ComnFileService comnFileService;
private final JwtTokenUtil jwtTokenUtil;
public boolean deleteQnaFile(int fileSno){
comnFileService.deleteFile(fileSno);
return true;
}
/**
* Qna 삭제하기, file들도 모두 논리삭제함.
* @param qnaSno
* @return
*/
@Transactional
public boolean deleteQna(int qnaSno){
CnsQnaBas entity = cnsQnaBasRepository.findByQnaSnoAndDelYnAndExpsrYn(qnaSno, "N", "Y");
if(entity == null) throw new CustomException(ErrorCode.DATA_NOTFIND);
if(entity.getFileGroupNo() != null) comnFileService.deleteFiles(entity.getFileGroupNo());
entity.setDelYn("Y");
List<CnsQnaBas> deleteList = new ArrayList<>();
deleteList.add(entity);
int targetSno = qnaSno;
// 대댓글 불러오기
while(true){
CnsQnaBas cnsQnaBas = cnsQnaBasRepository.findFirstByTargetSnoAndDelYnAndExpsrYn(targetSno, "N", "Y");
if(cnsQnaBas == null) break;
cnsQnaBas.setDelYn("Y");
deleteList.add(cnsQnaBas);
targetSno = cnsQnaBas.getQnaSno();
}
deleteList = cnsQnaBasRepository.saveAll(deleteList);
return true;
}
/**
* Qna 업데이트하기 TODO :: File 업데이트 기능 아직 구현안함
* @param qnaSno
* @param rq
* @return
*/
@Transactional
public boolean updateQna(int qnaSno, QnaInsertRQModel rq){
CnsQnaBas entity = cnsQnaBasRepository.findFirstByQnaSnoAndTargetSnoAndDelYnAndExpsrYn(qnaSno, 0, "N", "Y");
if(entity == null) throw new CustomException(ErrorCode.DATA_NOTFIND);
List<MultipartFile> files = rq.getFiles();
if(files != null && files.size() > 0) comnFileService.fileUpload(files, entity.getFileGroupNo()); // File 업로드
if(rq.getCategory() != null) entity.setCategory(rq.getCategory());
if(rq.getContent() != null) entity.setContent(rq.getContent());
if(rq.getTitle() != null) entity.setTitle(rq.getTitle());
cnsQnaBasRepository.save(entity);
return true;
}
/**
* Qna 상세불러오기
* @param qnaSno
* @return
*/
@Transactional // 조회수 증가하기떄문
public List<QnaDetailRSModel> getQnaDetail(int qnaSno){
cnsQnaBasRepository.pulsViewCount(qnaSno);
CnsQnaBas qnaBasModel = cnsQnaBasRepository.findFirstByQnaSnoAndTargetSnoAndDelYnAndExpsrYn(qnaSno, 0, "N", "Y");
List<ComFileBas> files = null;
if(qnaBasModel.getFileGroupNo() != 0) files = comFileBasRepository.findByFileGroupNoAndDelYn(qnaBasModel.getFileGroupNo(), "N");
QnaDetailRSModel node = new QnaDetailRSModel();
node.setQnaSno(qnaSno);
node.setTargetSno(qnaBasModel.getTargetSno());
node.setCategory(qnaBasModel.getCategory());
node.setTitle(qnaBasModel.getTitle());
node.setContent(qnaBasModel.getContent());
node.setViewCnt(qnaBasModel.getViewCnt());
node.setCreateUserId(qnaBasModel.getCreateUserId());
node.setCreateDt(qnaBasModel.getCreateDt());
node.setUpdateUserId(qnaBasModel.getUpdateUserId());
node.setUpdateDt(qnaBasModel.getUpdateDt());
node.setFiles(files);
List<QnaDetailRSModel> result = new ArrayList<>();
result.add(node);
int targetSno = qnaSno;
// 대댓글 불러오기
while(true){
CnsQnaBas cnsQnaBas = cnsQnaBasRepository.findFirstByTargetSnoAndDelYnAndExpsrYn(targetSno, "N", "Y");
if(cnsQnaBas == null) break;
QnaDetailRSModel detailNode = new QnaDetailRSModel();
detailNode.setQnaSno(cnsQnaBas.getQnaSno());
detailNode.setTargetSno(cnsQnaBas.getTargetSno());
detailNode.setCategory(cnsQnaBas.getCategory());
detailNode.setTitle(cnsQnaBas.getTitle());
detailNode.setContent(cnsQnaBas.getContent());
detailNode.setCreateUserId(cnsQnaBas.getCreateUserId());
detailNode.setCreateDt(cnsQnaBas.getCreateDt());
detailNode.setUpdateUserId(cnsQnaBas.getUpdateUserId());
detailNode.setUpdateDt(cnsQnaBas.getUpdateDt());
result.add(detailNode);
targetSno = cnsQnaBas.getQnaSno();
}
return result;
}
/**
* Qna리스트 조회
* @param rq
* @return
*/
public List<QnaBasModel> selectQnaList(QnaSelectListRQ rq){
return cnsQnaQueryRepository.getQnaList(rq.getCategory(), rq.getCategory());
}
/**
* QnaInsert
* @param rq
* @return
*/
@Transactional
public boolean insertQna(QnaInsertRQModel rq){
List<MultipartFile> files = rq.getFiles();
Integer fileGroupNo = null;
if(files != null && files.size() > 0) fileGroupNo = comnFileService.fileUpload(files, null); // File 업로드
String userId = jwtTokenUtil.getUserIdByToken();
if (userId == null) userId = "NONE";
CnsQnaBas cnsQnaBas = new CnsQnaBas();
cnsQnaBas.setCategory(rq.getCategory());
cnsQnaBas.setTitle(rq.getTitle());
cnsQnaBas.setContent(rq.getContent());
cnsQnaBas.setCreateDt(Instant.now());
cnsQnaBas.setCreateUserId(userId);
cnsQnaBas.setUpdateDt(Instant.now());
cnsQnaBas.setUpdateUserId(userId);
cnsQnaBas.setExpsrYn("Y");
if(fileGroupNo != null) cnsQnaBas.setFileGroupNo(fileGroupNo);
if(rq.getTargetSno() != null) cnsQnaBas.setTargetSno(rq.getTargetSno());
cnsQnaBasRepository.save(cnsQnaBas);
return true;
}
}

39
pav-server/src/main/java/com/palnet/biz/api/comn/file/constant/ExtensionConstant.java

@ -0,0 +1,39 @@
package com.palnet.biz.api.comn.file.constant;
import java.util.HashSet;
public enum ExtensionConstant {
JPG(".jpg"),
PNG(".png"),
JPEG(".jpeg"),
PDF(".pdf"),
XLSX(".xlsx"),
XLS(".xls"),
HWPX(".hwpx"),
HWP(".hwp"),
;
public final String extension;
public HashSet<ExtensionConstant> hashExtension;
private ExtensionConstant(String extension) {
this.extension = extension;
}
public static ExtensionConstant fromExtension(String extension) {
try {
for (ExtensionConstant constant : ExtensionConstant.values()) {
if (constant.extension.equals(extension)) {
return constant;
}
}
} catch (Exception e) {
return null;
}
return null;
}
}

257
pav-server/src/main/java/com/palnet/biz/api/comn/file/service/ComnFileService.java

@ -1,6 +1,32 @@
package com.palnet.biz.api.comn.file.service;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import javax.servlet.http.HttpServletResponse;
import javax.transaction.Transactional;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import com.palnet.biz.api.acnt.jwt.utils.JwtTokenUtil;
import com.palnet.biz.api.comn.file.constant.ExtensionConstant;
import com.palnet.biz.api.comn.file.model.LaancPdfModel;
import com.palnet.biz.jpa.entity.ComFileBas;
import com.palnet.biz.jpa.repository.com.ComFileBasRepository;
@ -8,20 +34,9 @@ import com.palnet.comn.code.ErrorCode;
import com.palnet.comn.exception.CustomException;
import com.palnet.comn.utils.InstantUtils;
import com.palnet.comn.utils.PdfUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.time.Instant;
import java.util.Base64;
@Slf4j
@RequiredArgsConstructor
@ -30,9 +45,14 @@ public class ComnFileService {
private final PdfUtils pdfUtils;
private final JwtTokenUtil jwtTokenUtil;
private final ComFileBasRepository comFileBasRepository;
private final ComFileBasRepository comFileBasRepository;
private final HttpServletResponse response;
private final String FILE_EXTENSION = ".pdf";
@Value("${base-url}")
private String BASE_PATH;
private final String NORMAL_FORDER_PATH = "normal/";
/**
* 파일명 만들기, 앞에 기본으로 금일 날짜있음
@ -41,7 +61,7 @@ public class ComnFileService {
* @param etcName
* @return
*/
private String getLaancSaveName(String... etcName) {
private String getLaancSaveName(ExtensionConstant extension, String... etcName) {
String date = InstantUtils.toDatetimeStringByFormat(Instant.now(), "yyyyMMddHHmmss");
@ -53,7 +73,7 @@ public class ComnFileService {
.append(name);
}
result.append(this.FILE_EXTENSION);
result.append(extension.extension);
return result.toString();
}
@ -68,7 +88,7 @@ public class ComnFileService {
String airUser = model.getPilotName();
String etc = String.valueOf(System.currentTimeMillis()); // 동명이인 방지
String fileName = getLaancSaveName(airUser, etc);
String fileName = getLaancSaveName(ExtensionConstant.PDF, airUser, etc);
// TODO 추후 img tag의 src로 들어가는 이미지들을 base64로 변환하여 html에 넣어줘야함
// images 넣기(임시)
@ -102,12 +122,25 @@ public class ComnFileService {
// DB Insert
// ComFileBas lastComFileBas = comFileBasRepository.findFirstByOrderByFileSnoDesc();
ComFileBas lastComFileBas = comFileBasRepository.findFirstByOrderByFileGroupNoDesc();
int fileGroupNo = (lastComFileBas == null) ? 1 : lastComFileBas.getFileGroupNo() + 1;
int fileGroupNo = this.lastFileGroupNo();
comFileBas.setFileGroupNo(fileGroupNo);
return comFileBasRepository.save(comFileBas);
}
/**
* 다건 DB 인서트, 최신데이터 가져온 Group NO 1추가함
* @param comFileBas
* @return
*/
public List<ComFileBas> saveAll(List<ComFileBas> comFileBas, Integer fileGroupNo){
for(ComFileBas fileBas : comFileBas){
fileBas.setFileGroupNo(fileGroupNo);
}
return comFileBasRepository.saveAll(comFileBas);
}
/**
@ -120,10 +153,146 @@ public class ComnFileService {
ComFileBas comFileBas = comFileBasRepository.findById(fileSno).orElse(null);
if (comFileBas == null) throw new CustomException(ErrorCode.DATA_NOTFIND);
pdfUtils.fileDownload(comFileBas);
InputStream inputStream = null;
OutputStream outputStream = null;
try {
File pdfFile = new File(comFileBas.getFilePath() + comFileBas.getFileSaveNm());
inputStream = new FileInputStream(pdfFile);
String fileOriName = comFileBas.getFileOriNm();
fileOriName = URLEncoder.encode(comFileBas.getFileOriNm(), StandardCharsets.UTF_8);
response.setContentType("application/octet-stream");
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileOriName + "\"");
// response.setContentLengthLong(Long.parseLong(comFileBas.getFileSize()));
// response.setStatus(HttpServletResponse.SC_OK);
outputStream = response.getOutputStream();
byte[] buffer = new byte[1024]; //1KB 설정
int length;
while ((length = inputStream.read(buffer)) != -1) {
outputStream.write(buffer);
}
} catch (FileNotFoundException e) {
throw new CustomException(ErrorCode.DATA_NO);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (outputStream != null) {
Objects.requireNonNull(outputStream).flush();
outputStream.close();
}
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public Integer fileUpload(List<MultipartFile> files, Integer fileGroupNo){
if(!this.validCheck(files)) throw new CustomException(ErrorCode.NON_VALID_PARAMETER);
if(fileGroupNo == null || fileGroupNo == 0) fileGroupNo = lastFileGroupNo();
// TODO : 절대경로
String absPath = "/Users/igyeongdo/WokrSpace/";
String fileUrl = new StringBuilder()
.append(absPath)
.append(this.BASE_PATH)
.append(this.NORMAL_FORDER_PATH)
.append(InstantUtils.toDateStringByFormat(Instant.now(), "yyyyMMdd"))
.append("/")
.toString();
List<ComFileBas> comFileBas = new ArrayList<>();
for(MultipartFile file : files){
String oriNm = file.getOriginalFilename();
String extension = oriNm.substring(oriNm.lastIndexOf("."));
String etc = String.valueOf(System.currentTimeMillis()); // 동명이인 방지
String fileName = this.getLaancSaveName(ExtensionConstant.fromExtension(extension), fileGroupNo.toString(), "qna", etc);
try {
Path directoryPath = Paths.get(fileUrl);
if (!Files.exists(directoryPath)) {
Files.createDirectories(directoryPath);
}
// 파일 저장 경로
String filePath = fileUrl + fileName;
File saveFile = new File(filePath);
file.transferTo(saveFile);
long fileSize = Files.size(Paths.get(filePath)) / 1024;
String userId = jwtTokenUtil.getUserIdByToken();
if (userId == null) userId = "NONE";
ComFileBas node = new ComFileBas();
node.setFileGroupNo(fileGroupNo);
node.setFilePath(fileUrl);
node.setFileExt(extension);
node.setCreateDt(Instant.now());
node.setCreateUserId(userId);
node.setFileSaveNm(fileName);
node.setFileOriNm(oriNm);
node.setFileSize(String.valueOf(fileSize));
comFileBas.add(node);
} catch (IOException e) {
e.printStackTrace();
}
}
this.saveAll(comFileBas, fileGroupNo);
return fileGroupNo;
}
/**
* 정해진 확장자만 있도록 validation처리,
* TODO :: 혹시 mime 타입으로 확인하여 처리해야 할까요?
* @param files
* @return
*/
private boolean validCheck(List<MultipartFile> files){
for(MultipartFile file : files){
try {
String oriNm = file.getOriginalFilename();
String extension = oriNm.substring(oriNm.lastIndexOf("."));
ExtensionConstant constants = ExtensionConstant.fromExtension(extension);
// 허락된 확장자가 아닐 경우 return false
if(constants == null) return false;
} catch (Exception e) {
// 확장자가 없는 파일일 경우 return false;
return false;
}
}
return true;
}
public String imagesToBase64ForSrc(String path) {
StringBuilder str = new StringBuilder();
ClassPathResource resource = new ClassPathResource(path);
@ -147,6 +316,54 @@ public class ComnFileService {
}
return str.toString();
}
/**
* 마지막 fileGroupNo 가져와 +1 반환
* @return
*/
private Integer lastFileGroupNo(){
ComFileBas lastComFileBas = comFileBasRepository.findFirstByOrderByFileGroupNoDesc();
int fileGroupNo = (lastComFileBas == null) ? 1 : lastComFileBas.getFileGroupNo() + 1;
return fileGroupNo;
}
@Transactional
public boolean deleteFiles(int fileGroupNo){
List<ComFileBas> files = comFileBasRepository.findByFileGroupNoAndDelYn(fileGroupNo, "N");
String userId = jwtTokenUtil.getUserIdByToken();
if (userId == null) userId = "NONE";
for(ComFileBas file : files){
file.setDelYn("Y");
file.setDelUserId(userId);
file.setDelDt(Instant.now());
}
comFileBasRepository.saveAll(files);
return true;
}
@Transactional
public boolean deleteFile(int fileSno){
ComFileBas file = comFileBasRepository.findByFileSnoAndDelYn(fileSno, "N");
String userId = jwtTokenUtil.getUserIdByToken();
if (userId == null) userId = "NONE";
file.setDelYn("Y");
file.setDelUserId(userId);
file.setDelDt(Instant.now());
comFileBasRepository.save(file);
return true;
}
}

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

@ -0,0 +1,53 @@
package com.palnet.biz.jpa.entity;
import java.io.Serializable;
import java.time.Instant;
import javax.persistence.*;
import lombok.Data;
@Entity
@Data
@Table(name="CNS_FAQ_BAS")
@NamedQuery(name="CnsFaqBas.findAll", query="SELECT c FROM CnsFaqBas c")
public class CnsFaqBas implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="FAQ_SNO")
private int faqSno;
@Column(name="CATEGORY")
private String category;
@Column(name="TITLE")
private String title;
@Column(name="CONTENT")
private String content;
@Column(name="VIEW_CNT")
private int viewCnt = 0;
@Column(name="EXPSR_YN")
private String expsrYn = "N";
@Column(name="DEL_YN")
private String delYn = "N";
@Column(name="CREATE_USER_ID")
private String createUserId;
@Column(name="CREATE_DT")
private Instant createDt;
@Column(name="UPDATE_USER_ID")
private String updateUserId;
@Column(name="UPDATE_DT")
private Instant updateDt;
}

60
pav-server/src/main/java/com/palnet/biz/jpa/entity/CnsQnaBas.java

@ -0,0 +1,60 @@
package com.palnet.biz.jpa.entity;
import java.io.Serializable;
import java.time.Instant;
import javax.persistence.*;
import lombok.Builder;
import lombok.Data;
@Entity
@Data
@Table(name="CNS_QNA_BAS")
@NamedQuery(name="CnsQnaBas.findAll", query="SELECT c FROM CnsQnaBas c")
public class CnsQnaBas implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="QNA_SNO")
private int qnaSno;
@Column(name="TARGET_SNO")
private int targetSno;
@Column(name="CATEGORY")
private String category;
@Column(name="TITLE")
private String title;
@Column(name="CONTENT")
private String content;
@Column(name="FILE_GROUP_NO")
private Integer fileGroupNo;
@Column(name="EXPSR_YN")
private String expsrYn = "N";
@Column(name="DEL_YN")
private String delYn = "N";
@Column(name="VIEW_CNT")
private int viewCnt = 0;
@Column(name="CREATE_USER_ID")
private String createUserId;
@Column(name="CREATE_DT")
private Instant createDt;
@Column(name="UPDATE_USER_ID")
private String updateUserId;
@Column(name="UPDATE_DT")
private Instant updateDt;
}

27
pav-server/src/main/java/com/palnet/biz/jpa/repository/cns/CnsFaqBasRepository.java

@ -0,0 +1,27 @@
package com.palnet.biz.jpa.repository.cns;
import javax.transaction.Transactional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import com.palnet.biz.jpa.entity.CnsFaqBas;
@Repository
public interface CnsFaqBasRepository extends JpaRepository<CnsFaqBas, Integer>{
@Modifying
@Transactional
@Query("update CnsFaqBas c set c.viewCnt = c.viewCnt + 1" +
"where c.faqSno = :sno")
int updateViewCnt(@Param("sno") int sno);
@Modifying
@Transactional
@Query("update CnsFaqBas c set c.delYn = 'Y' " +
"where c.faqSno = :sno")
int deleteFaq(@Param("sno") int sno);
}

111
pav-server/src/main/java/com/palnet/biz/jpa/repository/cns/CnsFaqQueryRepository.java

@ -0,0 +1,111 @@
package com.palnet.biz.jpa.repository.cns;
import java.util.List;
import org.springframework.stereotype.Repository;
import com.palnet.biz.api.cns.faq.model.FaqListModel;
import com.palnet.biz.jpa.entity.QCnsFaqBas;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.Projections;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Repository
@RequiredArgsConstructor
public class CnsFaqQueryRepository {
private final JPAQueryFactory query;
public List<FaqListModel> getFaqList(String category, String word) {
QCnsFaqBas bas = QCnsFaqBas.cnsFaqBas;
BooleanBuilder builder = new BooleanBuilder();
builder.and(bas.delYn.eq("N"));
builder.and(bas.expsrYn.eq("Y"));
if(category != null) {
builder.and(bas.category.eq(category));
}
if(word != null) {
builder.and(bas.title.contains(word));
}
List<FaqListModel> r = query
.select(Projections.bean(
FaqListModel.class,
bas.faqSno,
bas.category,
bas.title,
bas.content,
bas.viewCnt,
bas.expsrYn,
bas.delYn,
bas.createUserId,
bas.createDt,
bas.updateUserId,
bas.updateDt
))
.from(bas)
.where(builder)
.fetch();
return r;
}
public FaqListModel getFaqDetail(int sno) {
QCnsFaqBas bas = QCnsFaqBas.cnsFaqBas;
BooleanBuilder builder = new BooleanBuilder();
builder.and(bas.faqSno.eq(sno));
FaqListModel r = query
.select(Projections.bean(
FaqListModel.class,
bas.faqSno,
bas.category,
bas.title,
bas.content,
bas.viewCnt,
bas.expsrYn,
bas.delYn,
bas.createUserId,
bas.createDt,
bas.updateUserId,
bas.updateDt
))
.from(bas)
.where(builder)
.fetchOne();
return r;
}
public void updateFaq(FaqListModel model) {
QCnsFaqBas bas = QCnsFaqBas.cnsFaqBas;
BooleanBuilder builder = new BooleanBuilder();
builder.and(bas.faqSno.eq(model.getFaqSno()));
query.update(bas)
.set(bas.category, model.getCategory())
.set(bas.title, model.getTitle())
.set(bas.content, model.getContent())
.set(bas.viewCnt, model.getViewCnt())
.set(bas.expsrYn, model.getExpsrYn())
.set(bas.updateUserId, model.getUpdateUserId())
.set(bas.updateDt, model.getUpdateDt())
.where(builder)
.execute();
}
}

23
pav-server/src/main/java/com/palnet/biz/jpa/repository/cns/CnsQnaBasRepository.java

@ -0,0 +1,23 @@
package com.palnet.biz.jpa.repository.cns;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import com.palnet.biz.jpa.entity.CnsQnaBas;
@Repository
public interface CnsQnaBasRepository extends JpaRepository<CnsQnaBas, Integer>{
CnsQnaBas findFirstByTargetSnoAndDelYnAndExpsrYn(int targetSno, String delYn, String expsrYn);
CnsQnaBas findByQnaSnoAndDelYnAndExpsrYn(int qnaSno, String delYn, String expsrYn);
CnsQnaBas findFirstByQnaSnoAndTargetSnoAndDelYnAndExpsrYn(int qnaSno, int targetSno, String delYn, String expsrYn);
@Modifying
@Query("UPDATE CnsQnaBas q SET q.viewCnt = q.viewCnt + 1 WHERE q.qnaSno = :qnaSno")
void pulsViewCount(@Param("qnaSno")int qnaSno);
}

69
pav-server/src/main/java/com/palnet/biz/jpa/repository/cns/CnsQnaQueryRepository.java

@ -0,0 +1,69 @@
package com.palnet.biz.jpa.repository.cns;
import java.util.List;
import org.springframework.stereotype.Repository;
import com.palnet.biz.api.cns.qna.model.QnaBasModel;
import com.palnet.biz.jpa.entity.QCnsQnaBas;
import com.palnet.comn.code.ErrorCode;
import com.palnet.comn.exception.CustomException;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.Projections;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Repository
@RequiredArgsConstructor
public class CnsQnaQueryRepository {
private final JPAQueryFactory query;
/**
* 카테고리와 글로 게시글 검색, 만약 조건 없으면 전체검색
* @param category
* @param word
* @return
*/
public List<QnaBasModel> getQnaList(String category, String word) {
QCnsQnaBas bas = QCnsQnaBas.cnsQnaBas;
BooleanBuilder builder = new BooleanBuilder();
builder.and(bas.delYn.eq("N"));
builder.and(bas.expsrYn.eq("Y"));
if(category != null) {
builder.and(bas.category.eq(category));
}
if(word != null) {
builder.and(bas.title.contains(word));
}
List<QnaBasModel> r = query
.select(Projections.bean(
QnaBasModel.class,
bas.qnaSno,
bas.category,
bas.title,
bas.content,
bas.fileGroupNo,
bas.viewCnt,
bas.expsrYn,
bas.delYn,
bas.createUserId,
bas.createDt,
bas.updateUserId,
bas.updateDt
))
.from(bas)
.where(builder)
.fetch();
return r;
}
}

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

@ -1,5 +1,7 @@
package com.palnet.biz.jpa.repository.com;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@ -11,4 +13,9 @@ public interface ComFileBasRepository extends JpaRepository<ComFileBas, Integer>
ComFileBas findFirstByOrderByFileSnoDesc();
ComFileBas findFirstByOrderByFileGroupNoDesc();
List<ComFileBas> findByFileGroupNoAndDelYn(int fileGroupNo, String delYn);
ComFileBas findByFileSnoAndDelYn(int fileSno, String delYn);
}

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

@ -1,45 +1,43 @@
package com.palnet.comn.utils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
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.Value;
import org.springframework.stereotype.Component;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.io.font.PdfEncodings;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.font.FontProvider;
import com.palnet.biz.api.comn.file.constant.ExtensionConstant;
import com.palnet.biz.api.comn.file.model.PdfBaseModel;
import com.palnet.biz.jpa.entity.ComFileBas;
import com.palnet.comn.code.ErrorCode;
import com.palnet.comn.exception.CustomException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.Instant;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
@Slf4j
@RequiredArgsConstructor
@Component
public class PdfUtils {
private final HttpServletResponse response;
private final TemplateEngine templateEngine;
@Value("${base-url}")
private String BASE_PATH;
private final String FILE_EXTENSION = ".pdf";
private final String PDF_FORDER_PATH = BASE_PATH + "od/";
/**
@ -80,7 +78,13 @@ public class PdfUtils {
ComFileBas result = new ComFileBas();
String pdfPath = this.BASE_PATH + InstantUtils.toDateStringByFormat(Instant.now(), "yyyyMMdd") + "/";
String pdfPath = new StringBuilder()
.append(this.BASE_PATH)
.append(this.PDF_FORDER_PATH)
.append(InstantUtils.toDateStringByFormat(Instant.now(), "yyyyMMdd"))
.append("/")
.toString();
String pdfFilePath = pdfPath + fileName;
File path = new File(pdfPath);
@ -145,7 +149,7 @@ public class PdfUtils {
result.setFileGroupNo(0);
result.setFilePath(pdfPath);
result.setFileExt(this.FILE_EXTENSION);
result.setFileExt(ExtensionConstant.PDF.extension);
result.setCreateDt(Instant.now());
result.setFileSaveNm(fileName);
result.setFileOriNm(fileName);
@ -161,56 +165,5 @@ public class PdfUtils {
return result;
}
/**
* 파일 Download
*
* @param comFileBas
*/
public void fileDownload(ComFileBas comFileBas) {
InputStream inputStream = null;
OutputStream outputStream = null;
try {
File pdfFile = new File(comFileBas.getFilePath() + comFileBas.getFileSaveNm());
inputStream = new FileInputStream(pdfFile);
String fileOriName = comFileBas.getFileOriNm();
fileOriName = URLEncoder.encode(comFileBas.getFileOriNm(), StandardCharsets.UTF_8);
response.setContentType("application/octet-stream");
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileOriName + "\"");
// response.setContentLengthLong(Long.parseLong(comFileBas.getFileSize()));
// response.setStatus(HttpServletResponse.SC_OK);
outputStream = response.getOutputStream();
byte[] buffer = new byte[1024]; //1KB 설정
int length;
while ((length = inputStream.read(buffer)) != -1) {
outputStream.write(buffer);
}
} catch (FileNotFoundException e) {
throw new CustomException(ErrorCode.DATA_NO);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (outputStream != null) {
Objects.requireNonNull(outputStream).flush();
outputStream.close();
}
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

Loading…
Cancel
Save