From 5e024d4af11ee53458e222a1cc3f569ab3105a1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?lkd9125=28=EC=9D=B4=EA=B2=BD=EB=8F=84=29?= Date: Tue, 12 Dec 2023 10:13:05 +0900 Subject: [PATCH] =?UTF-8?q?cns/qna=20=ED=8C=A8=ED=82=A4=EC=A7=80=20?= =?UTF-8?q?=EC=A3=BC=EC=84=9D=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cns/qna/controller/CnsQnaController.java | 159 +++++++++++++++++- .../api/cns/qna/service/CnsQnaService.java | 33 ++-- .../comn/file/service/ComnFileService.java | 12 ++ .../repository/cns/CnsQnaQueryRepository.java | 85 +++++++++- 4 files changed, 269 insertions(+), 20 deletions(-) diff --git a/pav-server/src/main/java/com/palnet/biz/api/cns/qna/controller/CnsQnaController.java b/pav-server/src/main/java/com/palnet/biz/api/cns/qna/controller/CnsQnaController.java index ad159a14..430de96e 100644 --- a/pav-server/src/main/java/com/palnet/biz/api/cns/qna/controller/CnsQnaController.java +++ b/pav-server/src/main/java/com/palnet/biz/api/cns/qna/controller/CnsQnaController.java @@ -32,14 +32,28 @@ public class CnsQnaController { private final JwtTokenUtil jwtTokenUtil; + /** + * QnA 등록하는 기능, + * QnaInsertRQModel 모델에 요청값으로 QnA를 등록하는 기능. + * @param rq + * @return + */ @PostMapping(consumes = "multipart/form-data") @Tag(name = "QNA", description = "QNA 관련 API") @ApiOperation(value = "QnA 등록 - 사용자") public ResponseEntity insertQna(QnaInsertRQModel rq) { boolean result = false; try { - result = cnsQnaService.insertQna(rq); + result = cnsQnaService.insertQna(rq); // Qna 추가 하는기능 } catch (CustomException e) { + /** + * try{ + ... + } + * try 영역 안 코드들중 문제가 생기면 오는 곳. + * CustomException은 개발자가 "의도적으로" 낸 예외처리, + * log.error 로그로 원인 파악과 함께 API를 호출한 곳에 서버에러 내려줌 + */ Map resultMap = new HashMap<>(); log.error("IGNORE : ", e); resultMap.put("result", false); @@ -47,6 +61,13 @@ public class CnsQnaController { resultMap.put("errorMessage", e.getMessage()); return ResponseEntity.ok().body(new SuccessResponse<>(resultMap)); } catch (Exception e) { + /** + * try{ + ... + } + * try 영역 안 코드들중 문제가 생기면 오는 곳. + * log.error 로그로 원인 파악과 함께 API를 호출한 곳에 서버에러 내려줌 + */ log.error("IGNORE : ", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(new ErrorResponse("Server Error", "-1")); @@ -55,6 +76,12 @@ public class CnsQnaController { return ResponseEntity.ok().body(new SuccessResponse<>(result)); } + /** + * QnA리스트 조회하는 기능[관리자], + * QnaListAdminRQ 조건값에 따라 조회함. + * @param rq + * @return + */ @GetMapping @Tag(name = "QNA", description = "QNA 관련 API") @ApiOperation(value = "QnA 리스트 조회-관리자") @@ -62,8 +89,16 @@ public class CnsQnaController { List result = new ArrayList<>(); try { - result = cnsQnaService.selectQnaList(rq); + result = cnsQnaService.selectQnaList(rq); // Qna리스트 조회하는 기능 } catch (CustomException e) { + /** + * try{ + ... + } + * try 영역 안 코드들중 문제가 생기면 오는 곳. + * CustomException은 개발자가 "의도적으로" 낸 예외처리, + * log.error 로그로 원인 파악과 함께 API를 호출한 곳에 서버에러 내려줌 + */ Map resultMap = new HashMap<>(); log.error("IGNORE : ", e); resultMap.put("result", false); @@ -71,6 +106,13 @@ public class CnsQnaController { resultMap.put("errorMessage", e.getMessage()); return ResponseEntity.ok().body(new SuccessResponse<>(resultMap)); } catch (Exception e) { + /** + * try{ + ... + } + * try 영역 안 코드들중 문제가 생기면 오는 곳. + * log.error 로그로 원인 파악과 함께 API를 호출한 곳에 서버에러 내려줌 + */ log.error("IGNORE : ", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(new ErrorResponse("Server Error", "-1")); @@ -80,6 +122,12 @@ public class CnsQnaController { } + /** + * QnA리스트 조회하는 기능[관리자], + * QnaSelectListRQ 값에 따라서 조회함. + * @param rq + * @return + */ @GetMapping("/user") @Tag(name = "QNA", description = "QNA 관련 API") @ApiOperation(value = "QnA 리스트 조회-사용자") @@ -89,6 +137,14 @@ public class CnsQnaController { try { result = cnsQnaService.selectQnaForUser(rq); } catch (CustomException e) { + /** + * try{ + ... + } + * try 영역 안 코드들중 문제가 생기면 오는 곳. + * CustomException은 개발자가 "의도적으로" 낸 예외처리, + * log.error 로그로 원인 파악과 함께 API를 호출한 곳에 서버에러 내려줌 + */ Map resultMap = new HashMap<>(); log.error("IGNORE : ", e); resultMap.put("result", false); @@ -96,6 +152,13 @@ public class CnsQnaController { resultMap.put("errorMessage", e.getMessage()); return ResponseEntity.ok().body(new SuccessResponse<>(resultMap)); } catch (Exception e) { + /** + * try{ + ... + } + * try 영역 안 코드들중 문제가 생기면 오는 곳. + * log.error 로그로 원인 파악과 함께 API를 호출한 곳에 서버에러 내려줌 + */ log.error("IGNORE : ", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(new ErrorResponse("Server Error", "-1")); @@ -105,14 +168,28 @@ public class CnsQnaController { } + /** + * QnA 상세보기 기능, + * QnA일련번호[qnaSno]값으로 조회함. + * @param qnaSno + * @return + */ @GetMapping("/{qnaSno}") @Tag(name = "QNA", description = "QNA 관련 API") @ApiOperation(value = "QnA 상세보기") public ResponseEntity detailQna(@PathVariable int qnaSno) { QnaDetailRSModel result = null; try { - result = cnsQnaService.getQnaDetail(qnaSno); + result = cnsQnaService.getQnaDetail(qnaSno); // Qna 상세불러오기 기능 } catch (CustomException e) { + /** + * try{ + ... + } + * try 영역 안 코드들중 문제가 생기면 오는 곳. + * CustomException은 개발자가 "의도적으로" 낸 예외처리, + * log.error 로그로 원인 파악과 함께 API를 호출한 곳에 서버에러 내려줌 + */ Map resultMap = new HashMap<>(); log.error("IGNORE : ", e); resultMap.put("result", false); @@ -120,6 +197,13 @@ public class CnsQnaController { resultMap.put("errorMessage", e.getMessage()); return ResponseEntity.ok().body(new SuccessResponse<>(resultMap)); } catch (Exception e) { + /** + * try{ + ... + } + * try 영역 안 코드들중 문제가 생기면 오는 곳. + * log.error 로그로 원인 파악과 함께 API를 호출한 곳에 서버에러 내려줌 + */ log.error("IGNORE : ", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(new ErrorResponse("Server Error", "-1")); @@ -128,14 +212,28 @@ public class CnsQnaController { return ResponseEntity.ok().body(new SuccessResponse<>(result)); } + /** + * QnA수정 하는 기능[사용자], + * QnaInsertRQModel 모델에 입력받은 사항들을 수정함. + * @param rq + * @return + */ @PutMapping(consumes = "multipart/form-data") @Tag(name = "QNA", description = "QNA 관련 API") @ApiOperation(value = "QnA 수정 - 사용자") public ResponseEntity updateQna(QnaInsertRQModel rq) { boolean result = false; try { - result = cnsQnaService.updateQna(rq); + result = cnsQnaService.updateQna(rq); // Qna 업데이트하는 기능 } catch (CustomException e) { + /** + * try{ + ... + } + * try 영역 안 코드들중 문제가 생기면 오는 곳. + * CustomException은 개발자가 "의도적으로" 낸 예외처리, + * log.error 로그로 원인 파악과 함께 API를 호출한 곳에 서버에러 내려줌 + */ Map resultMap = new HashMap<>(); log.error("IGNORE : ", e); resultMap.put("result", false); @@ -143,6 +241,13 @@ public class CnsQnaController { resultMap.put("errorMessage", e.getMessage()); return ResponseEntity.ok().body(new SuccessResponse<>(resultMap)); } catch (Exception e) { + /** + * try{ + ... + } + * try 영역 안 코드들중 문제가 생기면 오는 곳. + * log.error 로그로 원인 파악과 함께 API를 호출한 곳에 서버에러 내려줌 + */ log.error("IGNORE : ", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(new ErrorResponse("Server Error", "-1")); @@ -152,6 +257,12 @@ public class CnsQnaController { } + /** + * QnA삭제하는 기능, + * QnA일련번호[qnaSno]로 삭제함. + * @param qnaSno + * @return + */ @DeleteMapping("/{qnaSno}") @Tag(name = "QNA", description = "QNA 관련 API") @ApiOperation(value = "QnA 삭제하기") @@ -159,8 +270,16 @@ public class CnsQnaController { // TODO 관리자만 삭제 가능 여부인지 확인 필요 boolean result = false; try { - result = cnsQnaService.deleteQna(qnaSno); + result = cnsQnaService.deleteQna(qnaSno); // Qna 삭제하기, file들도 모두 논리삭제하는 기능. } catch (CustomException e) { + /** + * try{ + ... + } + * try 영역 안 코드들중 문제가 생기면 오는 곳. + * CustomException은 개발자가 "의도적으로" 낸 예외처리, + * log.error 로그로 원인 파악과 함께 API를 호출한 곳에 서버에러 내려줌 + */ Map resultMap = new HashMap<>(); log.error("IGNORE : ", e); resultMap.put("result", false); @@ -168,6 +287,13 @@ public class CnsQnaController { resultMap.put("errorMessage", e.getMessage()); return ResponseEntity.ok().body(new SuccessResponse<>(resultMap)); } catch (Exception e) { + /** + * try{ + ... + } + * try 영역 안 코드들중 문제가 생기면 오는 곳. + * log.error 로그로 원인 파악과 함께 API를 호출한 곳에 서버에러 내려줌 + */ log.error("IGNORE : ", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(new ErrorResponse("Server Error", "-1")); @@ -176,6 +302,12 @@ public class CnsQnaController { return ResponseEntity.ok().body(new SuccessResponse<>(result)); } + /** + * QnA 답변등록하는 기능[관리자], + * QnaInsertAnserRQModel에 입력받은 값으로 답변을 등록함. + * @param rq + * @return + */ @PutMapping("/answer") @Tag(name = "QNA", description = "QNA 관련 API") @ApiOperation(value = "QnA 답변 등록 - 관리자") @@ -188,8 +320,16 @@ public class CnsQnaController { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(new ErrorResponse(RSErrorCode.AUTH_ERROR)); } - result = cnsQnaService.insertAnswer(rq); + result = cnsQnaService.insertAnswer(rq); // 답변추가하는 기능. } catch (CustomException e) { + /** + * try{ + ... + } + * try 영역 안 코드들중 문제가 생기면 오는 곳. + * CustomException은 개발자가 "의도적으로" 낸 예외처리, + * log.error 로그로 원인 파악과 함께 API를 호출한 곳에 서버에러 내려줌 + */ Map resultMap = new HashMap<>(); log.error("IGNORE : ", e); resultMap.put("result", false); @@ -197,6 +337,13 @@ public class CnsQnaController { resultMap.put("errorMessage", e.getMessage()); return ResponseEntity.ok().body(new SuccessResponse<>(resultMap)); } catch (Exception e) { + /** + * try{ + ... + } + * try 영역 안 코드들중 문제가 생기면 오는 곳. + * log.error 로그로 원인 파악과 함께 API를 호출한 곳에 서버에러 내려줌 + */ log.error("IGNORE : ", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(new ErrorResponse("Server Error", "-1")); diff --git a/pav-server/src/main/java/com/palnet/biz/api/cns/qna/service/CnsQnaService.java b/pav-server/src/main/java/com/palnet/biz/api/cns/qna/service/CnsQnaService.java index f3f719ec..45f9cceb 100644 --- a/pav-server/src/main/java/com/palnet/biz/api/cns/qna/service/CnsQnaService.java +++ b/pav-server/src/main/java/com/palnet/biz/api/cns/qna/service/CnsQnaService.java @@ -41,11 +41,11 @@ public class CnsQnaService { private final JwtTokenUtil jwtTokenUtil; /** - * Qna 삭제하기, file들도 모두 논리삭제함. - * + * Qna 삭제하기, file들도 모두 논리삭제하는 기능. * @param qnaSno * @return */ + // @Transactional는 데이터베이스의 정합성을 위해 사용합니다, 추가 수정 삭제 등에서 사용합니다, 이 기능에서는 삭제컬럼은 수정했기 때문에 사용했습니다. @Transactional public boolean deleteQna(int qnaSno) { @@ -62,11 +62,11 @@ public class CnsQnaService { } /** - * Qna 업데이트하기 - * + * Qna 업데이트하는 기능. * @param rq * @return */ + // @Transactional는 데이터베이스의 정합성을 위해 사용합니다, 추가 수정 삭제 등에서 사용합니다, 이 기능에서는 수정하기 때문에 사용합니다. @Transactional public boolean updateQna(QnaInsertRQModel rq) { @@ -77,7 +77,7 @@ public class CnsQnaService { List prevFileSnoList = new ArrayList<>(); List currentFileSnoList = new ArrayList<>(); if (entity.getFileGroupNo() != null) { - List prevFileInfos = comnFileService.getNormalFileListByGroupNo(entity.getFileGroupNo()); + List prevFileInfos = comnFileService.getNormalFileListByGroupNo(entity.getFileGroupNo()); // 파일그룹번호[fileGroupNo]에 해당하는 파일등 모두 가져오는 기능. prevFileSnoList = prevFileInfos.stream().map(ComnFileModel::getFileSno).collect(Collectors.toList()); } if (rq.getFileInfos() != null) { @@ -112,12 +112,12 @@ public class CnsQnaService { } /** - * Qna 상세불러오기 - * + * Qna 상세불러오기 기능. * @param qnaSno * @return */ - @Transactional // 조회수 증가하기떄문 + // @Transactional는 데이터베이스의 정합성을 위해 사용합니다, 추가 수정 삭제 등에서 사용합니다, 이 기능에서는 조회수 증가하기 떄문에 사용했습니다. + @Transactional public QnaDetailRSModel getQnaDetail(int qnaSno) { cnsQnaBasRepository.pulsViewCount(qnaSno); @@ -125,7 +125,7 @@ public class CnsQnaService { CnsQnaBas entity = cnsQnaBasRepository.findFirstByQnaSnoAndTargetSnoAndDelYnAndExpsrYn(qnaSno, 0, "N", "Y"); List files = null; if (entity.getFileGroupNo() != null && entity.getFileGroupNo() != 0) - files = comnFileService.getNormalFileListByGroupNo(entity.getFileGroupNo()); + files = comnFileService.getNormalFileListByGroupNo(entity.getFileGroupNo()); // 파일그룹번호[fileGroupNo]에 해당하는 파일등 모두 가져오는 기능. QnaDetailRSModel model = CnsQnaMapper.MAPPER.toModel(entity); PtyCstmrBas user = ptyCstmrBasRepository.findByUserId(entity.getCreateUserId()).orElse(null); @@ -141,7 +141,7 @@ public class CnsQnaService { } /** - * Qna리스트 조회 + * Qna리스트 조회하는 기능[관리자]. * * @param rq * @return @@ -150,14 +150,18 @@ public class CnsQnaService { return cnsQnaQueryRepository.getQnaList(rq); } + /** + * Qna리스트 조회하는 기능[유저] + * @param rq + * @return + */ public List selectQnaForUser(QnaSelectListRQ rq) { String userId = jwtTokenUtil.getUserIdByToken(); return cnsQnaQueryRepository.getQnaListForUser(rq, userId); } /** - * QnaInsert - * + * Qna 추가 하는기능. * @param rq * @return */ @@ -188,6 +192,11 @@ public class CnsQnaService { } + /** + * 답변추가하는 기능. + * @param rq + * @return + */ public boolean insertAnswer(QnaInsertAnserRQModel rq) { CnsQnaBas entity = cnsQnaBasRepository.findByQnaSnoAndDelYnAndExpsrYn(rq.getQnaSno(), "N", "Y"); if (entity == null) throw new CustomException(ErrorCode.DATA_NOTFIND); diff --git a/pav-server/src/main/java/com/palnet/biz/api/comn/file/service/ComnFileService.java b/pav-server/src/main/java/com/palnet/biz/api/comn/file/service/ComnFileService.java index ba77dbaf..17df0035 100644 --- a/pav-server/src/main/java/com/palnet/biz/api/comn/file/service/ComnFileService.java +++ b/pav-server/src/main/java/com/palnet/biz/api/comn/file/service/ComnFileService.java @@ -351,6 +351,13 @@ public class ComnFileService { return true; } + + /** + * 파일일련번호 배열로 삭제처리 진행함. + * @param fileSnoList + * @return + */ + // @Transactional는 데이터베이스의 정합성을 위해 사용합니다, 추가 수정 삭제 등에서 사용합니다, 이 기능에서는 삭제컬럼은 수정했기 때문에 사용했습니다. @Transactional public boolean deleteFiles(List fileSnoList){ @@ -389,6 +396,11 @@ public class ComnFileService { return true; } + /** + * 파일그룹번호[fileGroupNo]에 해당하는 파일등 모두 가져오는 기능. + * @param fileGroupNo + * @return + */ public List getNormalFileListByGroupNo(Integer fileGroupNo) { List files = comFileBasRepository.findByFileGroupNoAndDelYn(fileGroupNo, "N"); List result = new ArrayList<>(); diff --git a/pav-server/src/main/java/com/palnet/biz/jpa/repository/cns/CnsQnaQueryRepository.java b/pav-server/src/main/java/com/palnet/biz/jpa/repository/cns/CnsQnaQueryRepository.java index 85b89057..3e4a1bab 100644 --- a/pav-server/src/main/java/com/palnet/biz/jpa/repository/cns/CnsQnaQueryRepository.java +++ b/pav-server/src/main/java/com/palnet/biz/jpa/repository/cns/CnsQnaQueryRepository.java @@ -25,8 +25,7 @@ public class CnsQnaQueryRepository { private final JPAQueryFactory query; /** - * 카테고리와 글로 게시글 검색, 만약 조건 없으면 전체검색 - * + * 카테고리와 글로 게시글 검색, 만약 조건 없으면 전체검색 하는 SQL기능. * @param rq * @return */ @@ -58,6 +57,43 @@ public class CnsQnaQueryRepository { } } + /** + * 삭제여부[delYn]값이 'N' 조건인지, + * 표출여부[expsrYn]값이 'Y' 조건인지, + * 카테고리[category]값 조건, + * 작성자[memberName]값 조건, + * 답변여부[anserStatus]값 조건으로 조회하는 SQL 입니다. + * + * SELECT + * CQB.QNA_SNO , + * CQB.CATEGORY , + * CQB.TITLE , + * CQB.CONTENT , + * CQB.CONTACT , + * CQB.FILE_GROUP_NO , + * CQB.ANSER_STATUS , + * CQB.ANSER_CONTENT , + * CQB.ANSER_PROC_DT , + * CQB.ANSER_USER_NM , + * CQB.VIEW_CNT , + * CQB.EXPSR_YN , + * CQB.DEL_YN , + * CQB.CREATE_USER_ID , + * CQB.CREATE_DT , + * CQB.UPDATE_USER_ID , + * CQB.UPDATE_DT , + * PCD.MEMBER_NAME AS 'createUserNm' + * FROM CNS_QNA_BAS CQB + * LEFT OUTER JOIN PTY_CSTMR_BAS PCB + * ON CQB.CREATE_USER_ID = PCB.USER_ID + * LEFT OUTER JOIN PTY_CSTMR_DTL PCD + * ON PCB.CSTMR_SNO = PCD.CSTMR_SNO + * WHERE CQB.DEL_YN = 'N' + * AND CQB.EXPSR_YN = 'Y' + * AND CQB.CATEGORY = #{category} + * AND PCD.MEMBER_NAME = #{createUserNm} + * AND CQB.ANSER_STATUS = #{anserStatus} + */ List r = query .select(Projections.bean( QnaBasModel.class, @@ -90,6 +126,12 @@ public class CnsQnaQueryRepository { return r; } + /** + * 유저아이디[userId]에 따라 QnA리스트를 조회하는 SQL 기능. + * @param rq + * @param userId + * @return + */ public List getQnaListForUser(QnaSelectListRQ rq, String userId) { QCnsQnaBas bas = QCnsQnaBas.cnsQnaBas; QPtyCstmrBas cBas = QPtyCstmrBas.ptyCstmrBas; @@ -113,6 +155,45 @@ public class CnsQnaQueryRepository { } } + /** + * 삭제여부[delYn]값이 'N' 조건인지, + * 표출여부[expsrYn]값이 'Y' 조건인지, + * 작성자[createUserId]값 조건, + * 카테고리[createUserId] 값 조건, + * 제목[title]값 조건, + * 내용[content]값 조건으로 조회하는 SQL 입니다. + * + * SELECT + * CQB.QNA_SNO , + * CQB.CATEGORY , + * CQB.TITLE , + * CQB.CONTENT , + * CQB.CONTACT , + * CQB.FILE_GROUP_NO , + * CQB.ANSER_STATUS , + * CQB.ANSER_CONTENT , + * CQB.ANSER_PROC_DT , + * CQB.ANSER_USER_NM , + * CQB.VIEW_CNT , + * CQB.EXPSR_YN , + * CQB.DEL_YN , + * CQB.CREATE_USER_ID , + * CQB.CREATE_DT , + * CQB.UPDATE_USER_ID , + * CQB.UPDATE_DT , + * PCD.MEMBER_NAME AS 'createUserNm' + * FROM CNS_QNA_BAS CQB + * LEFT OUTER JOIN PTY_CSTMR_BAS PCB + * ON CQB.CREATE_USER_ID = PCB.USER_ID + * LEFT OUTER JOIN PTY_CSTMR_DTL PCD + * ON PCB.CSTMR_SNO = PCD.CSTMR_SNO + * WHERE CQB.DEL_YN = 'N' + * AND CQB.EXPSR_YN = 'Y' + * AND CQB.CREATE_USER_ID = #{userId} + * AND CQB.CATEGORY = #{category} + * AND CQB.TITLE LIKE CONCAT('%', #{word}, '%') + * AND CQB.CONTENT LIKE CONCAT('%', #{word}, '%') + */ List r = query .select(Projections.bean( QnaBasModel.class,