From 55d664215d4106ec3391bc028f06a4b5d1affd56 Mon Sep 17 00:00:00 2001 From: qkr7828 Date: Wed, 28 Feb 2024 13:58:26 +0900 Subject: [PATCH] =?UTF-8?q?feat=20:=20contents=20=EC=9E=91=EC=97=85?= =?UTF-8?q?=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/kac-app/build.gradle | 1 + data/user/build.gradle | 6 + web/api-cns/build.gradle | 13 + .../cns/faq/controller/CnsFaqController.java | 278 ++++++++++++++++++ .../kac/api/v1/cns/faq/model/FaqListDto.java | 44 +++ .../kac/api/v1/cns/faq/model/FaqListRq.java | 15 + .../kac/api/v1/cns/faq/model/FaqListRs.java | 32 ++ .../api/v1/cns/faq/model/FaqUpdateDto.java | 44 +++ .../api/v1/cns/faq/service/CnsFaqService.java | 129 ++++++++ 9 files changed, 562 insertions(+) create mode 100644 data/user/build.gradle create mode 100644 web/api-cns/build.gradle create mode 100644 web/api-cns/src/main/java/kr/co/palnet/kac/api/v1/cns/faq/controller/CnsFaqController.java create mode 100644 web/api-cns/src/main/java/kr/co/palnet/kac/api/v1/cns/faq/model/FaqListDto.java create mode 100644 web/api-cns/src/main/java/kr/co/palnet/kac/api/v1/cns/faq/model/FaqListRq.java create mode 100644 web/api-cns/src/main/java/kr/co/palnet/kac/api/v1/cns/faq/model/FaqListRs.java create mode 100644 web/api-cns/src/main/java/kr/co/palnet/kac/api/v1/cns/faq/model/FaqUpdateDto.java create mode 100644 web/api-cns/src/main/java/kr/co/palnet/kac/api/v1/cns/faq/service/CnsFaqService.java diff --git a/app/kac-app/build.gradle b/app/kac-app/build.gradle index 59af035..945f5c1 100644 --- a/app/kac-app/build.gradle +++ b/app/kac-app/build.gradle @@ -40,6 +40,7 @@ dependencies { implementation project(":web:security") implementation project(":web:api-common") implementation project(":web:api-user") + implementation project(":web:api-cns") } ext { diff --git a/data/user/build.gradle b/data/user/build.gradle new file mode 100644 index 0000000..6875d3b --- /dev/null +++ b/data/user/build.gradle @@ -0,0 +1,6 @@ + + +dependencies { + +} + diff --git a/web/api-cns/build.gradle b/web/api-cns/build.gradle new file mode 100644 index 0000000..91d852a --- /dev/null +++ b/web/api-cns/build.gradle @@ -0,0 +1,13 @@ + + +dependencies { + implementation "$boot:spring-boot-starter-web" + implementation "$boot:spring-boot-starter-data-jpa" + + + compileOnly 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0' + implementation project(":data:cns") + implementation project(":common:core") + compileOnly project(":web:security") +} + diff --git a/web/api-cns/src/main/java/kr/co/palnet/kac/api/v1/cns/faq/controller/CnsFaqController.java b/web/api-cns/src/main/java/kr/co/palnet/kac/api/v1/cns/faq/controller/CnsFaqController.java new file mode 100644 index 0000000..c9f2fcf --- /dev/null +++ b/web/api-cns/src/main/java/kr/co/palnet/kac/api/v1/cns/faq/controller/CnsFaqController.java @@ -0,0 +1,278 @@ +package kr.co.palnet.kac.api.v1.cns.faq.controller; + + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import kr.co.palnet.kac.api.v1.cns.faq.model.FaqListDto; +import kr.co.palnet.kac.api.v1.cns.faq.model.FaqListRq; +import kr.co.palnet.kac.api.v1.cns.faq.model.FaqListRs; +import kr.co.palnet.kac.api.v1.cns.faq.model.FaqUpdateDto; +import kr.co.palnet.kac.api.v1.cns.faq.service.CnsFaqService; +import kr.co.palnet.kac.core.exception.BaseException; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.ErrorResponse; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestController +@Slf4j +@RequiredArgsConstructor +@RequestMapping(value = "/v1/cns/faq") +@Tag(name = "FAQ", description = "FAQ 관련 API") +public class CnsFaqController { + + private final CnsFaqService service; + + /** + * FAQ 목록 조회하는 기능, + * FaqListRQModel 값에 있는 조건값으로 조회함. + * @param model + * @return + */ + @GetMapping + @Operation(summary = "FAQ 조회", description = "FAQ를 조회하는 API 입니다.") + public ResponseEntity getFaqList(FaqListRq model) { + List result = null; + try { + result = service.getFaqList(model); // FaQ 항목들 조회하는 기능 + } catch (BaseException e) { + /** + * try{ + ... + } + * try 영역 안 코드들중 문제가 생기면 오는 곳. + * CustomException은 개발자가 "의도적으로" 낸 예외처리, + * log.error 로그로 원인 파악과 함께 API를 호출한 곳에 서버에러 내려줌 + */ + Map resultMap = new HashMap<>(); + log.error("IGNORE : ", e); + resultMap.put("result", false); + resultMap.put("errorCode", e.getErrorCode()); + 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")); + + } + return ResponseEntity.ok().body(new SuccessResponse<>(result)); + } + + /** + * FAQ 상세 조회하는 기능, + * 일련번호[sno]로 조회함. + * @param sno + * @return + */ + @GetMapping("/{sno}") + @Operation(summary = "FAQ 상세 조회", description = "특정 FAQ만 조회하는 API 입니다. sno 값 : 1, 2, 등 ....") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = { + @io.swagger.v3.oas.annotations.media.Content(mediaType = "application/json", + array = @io.swagger.v3.oas.annotations.media.ArraySchema( + schema = @io.swagger.v3.oas.annotations.media.Schema(implementation = FaqListDto.class))) + }) + }) + public ResponseEntity getFaqDetail( + @Parameter(name="sno", description = "FAQ 시리얼 넘버", in = ParameterIn.PATH, example = "17")@PathVariable int sno) { + FaqListRs result = null; + try { + result = service.getFaqDetail(sno); // 일련번호[sno]로 상세정보를 조회하는 기능 + } catch (CustomException e) { + /** + * try{ + ... + } + * try 영역 안 코드들중 문제가 생기면 오는 곳. + * CustomException은 개발자가 "의도적으로" 낸 예외처리, + * log.error 로그로 원인 파악과 함께 API를 호출한 곳에 서버에러 내려줌 + */ + Map resultMap = new HashMap<>(); + log.error("IGNORE : ", e); + resultMap.put("result", false); + resultMap.put("errorCode", e.getErrorCode()); + 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")); + + } + return ResponseEntity.ok().body(new SuccessResponse<>(result)); + } + + /** + * FAQ 작성하는 기능, + * FaqListModel모델에 있는 값으로 작성하는 기능. + * @param model + * @return + */ + @PostMapping + @Operation(summary = "FAQ 작성", description = "FAQ를 작성하는 API 입니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = { + @io.swagger.v3.oas.annotations.media.Content(mediaType = "application/json", + array = @io.swagger.v3.oas.annotations.media.ArraySchema( + schema = @io.swagger.v3.oas.annotations.media.Schema(implementation = SuccessResponse.class))) + }) + }) + public ResponseEntity insertFaq(@RequestBody FaqListDto model) { + boolean result = false; + try { + result = service.insertFaq(model); // FaQ 추가하는 기능 + } catch (CustomException e) { + /** + * try{ + ... + } + * try 영역 안 코드들중 문제가 생기면 오는 곳. + * CustomException은 개발자가 "의도적으로" 낸 예외처리, + * log.error 로그로 원인 파악과 함께 API를 호출한 곳에 서버에러 내려줌 + */ + Map resultMap = new HashMap<>(); + log.error("IGNORE : ", e); + resultMap.put("result", false); + resultMap.put("errorCode", e.getErrorCode()); + 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")); + + } + return ResponseEntity.ok().body(new SuccessResponse<>(result)); + + + } + + /** + * FAQ 업데이트 기능, + * FaqListModel 모델에 있는 값으로 업데이트함. + * @param model + * @return + */ + @PutMapping + @Operation(summary = "FAQ 수정", description = "FAQ를 수정하는 API 입니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = { + @io.swagger.v3.oas.annotations.media.Content(mediaType = "application/json", + array = @io.swagger.v3.oas.annotations.media.ArraySchema( + schema = @io.swagger.v3.oas.annotations.media.Schema(implementation = SuccessResponse.class))) + }) + }) + public ResponseEntity updateFaq(@RequestBody FaqUpdateDto model) { + boolean result = false; + try { + result = service.updateFaq(model); // FaQ 수정하는 기능 + } catch (CustomException e) { + /** + * try{ + ... + } + * try 영역 안 코드들중 문제가 생기면 오는 곳. + * CustomException은 개발자가 "의도적으로" 낸 예외처리, + * log.error 로그로 원인 파악과 함께 API를 호출한 곳에 서버에러 내려줌 + */ + Map resultMap = new HashMap<>(); + log.error("IGNORE : ", e); + resultMap.put("result", false); + resultMap.put("errorCode", e.getErrorCode()); + 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")); + + } + return ResponseEntity.ok().body(new SuccessResponse<>(result)); + + } + + /** + * FAQ 삭제하는 기능, + * 일련번호[sno]로 삭제하는 기능. + * @param sno + * @return + */ + @DeleteMapping("/{sno}") + @Operation(summary = "FAQ 삭제", description = "특정 FAQ를 논리 삭제하는 API 입니다. sno 예시 : 1, 2, 등 ...") + public ResponseEntity deleteFaq(@PathVariable("sno") int sno) { + // TODO 성공/실패 상태값 전송 + try { + service.deleteFaq(sno); // 일련번호[sno]로 삭제하는 기능 + } catch (CustomException e) { + /** + * try{ + ... + } + * try 영역 안 코드들중 문제가 생기면 오는 곳. + * log.error 로그로 원인 파악과 함께 API를 호출한 곳에 서버에러 내려줌 + */ + Map resultMap = new HashMap<>(); + log.error("IGNORE : ", e); + resultMap.put("result", false); + resultMap.put("errorCode", e.getErrorCode()); + 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")); + + } + return ResponseEntity.ok().build(); + + } +} \ No newline at end of file diff --git a/web/api-cns/src/main/java/kr/co/palnet/kac/api/v1/cns/faq/model/FaqListDto.java b/web/api-cns/src/main/java/kr/co/palnet/kac/api/v1/cns/faq/model/FaqListDto.java new file mode 100644 index 0000000..b95c30a --- /dev/null +++ b/web/api-cns/src/main/java/kr/co/palnet/kac/api/v1/cns/faq/model/FaqListDto.java @@ -0,0 +1,44 @@ +package kr.co.palnet.kac.api.v1.cns.faq.model; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.Instant; + +@Data +public class FaqListDto { + + @Schema(hidden = true) + private int faqSno; + + @Schema(description = "FAQ 분류" , example = "장치신고") + private String category; + + @Schema(description = "작성할 글의 제목" , example = "제목 샘플") + private String title; + + @Schema(description = "작성할 글의 내용" , example = "내용 샘플") + private String content; + + @Schema(hidden = true) + private int viewCnt; + + @Schema(description = "표출 여부" , example = "Y") + private String expsrYn; + + @Schema(hidden = true) + private String delYn; + + @Schema(hidden = true) + private String createUserId; + + @Schema(description = "생성일자", example = "2023-12-12", implementation = String.class) + private Instant createDt; + + @Schema(hidden = true) + private String updateUserId; + + @Schema(description = "업데이트 일자", example = "2023-12-12", implementation = String.class) + private Instant updateDt; + +} \ No newline at end of file diff --git a/web/api-cns/src/main/java/kr/co/palnet/kac/api/v1/cns/faq/model/FaqListRq.java b/web/api-cns/src/main/java/kr/co/palnet/kac/api/v1/cns/faq/model/FaqListRq.java new file mode 100644 index 0000000..e70a43d --- /dev/null +++ b/web/api-cns/src/main/java/kr/co/palnet/kac/api/v1/cns/faq/model/FaqListRq.java @@ -0,0 +1,15 @@ +package kr.co.palnet.kac.api.v1.cns.faq.model; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +public class FaqListRq { + + @Schema(description = "FAQ 카테고리" , example = "비행승인") + private String category; + + @Schema(description = "해당 단어가 포함된 제목 검색" , example = "비행") + private String word; + +} \ No newline at end of file diff --git a/web/api-cns/src/main/java/kr/co/palnet/kac/api/v1/cns/faq/model/FaqListRs.java b/web/api-cns/src/main/java/kr/co/palnet/kac/api/v1/cns/faq/model/FaqListRs.java new file mode 100644 index 0000000..ff2a096 --- /dev/null +++ b/web/api-cns/src/main/java/kr/co/palnet/kac/api/v1/cns/faq/model/FaqListRs.java @@ -0,0 +1,32 @@ +package kr.co.palnet.kac.api.v1.cns.faq.model; + +import lombok.Data; + +import java.time.Instant; + +@Data +public class FaqListRs { + + 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; + +} diff --git a/web/api-cns/src/main/java/kr/co/palnet/kac/api/v1/cns/faq/model/FaqUpdateDto.java b/web/api-cns/src/main/java/kr/co/palnet/kac/api/v1/cns/faq/model/FaqUpdateDto.java new file mode 100644 index 0000000..8f12aeb --- /dev/null +++ b/web/api-cns/src/main/java/kr/co/palnet/kac/api/v1/cns/faq/model/FaqUpdateDto.java @@ -0,0 +1,44 @@ +package kr.co.palnet.kac.api.v1.cns.faq.model; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.Instant; + +@Data +public class FaqUpdateDto { + + @Schema(description = "수정할 글의 번호" , example = "17") + private int faqSno; + + @Schema(description = "FAQ 분류" , example = "장치신고") + private String category; + + @Schema(description = "수정할 글의 제목" , example = "제목 수정 샘플") + private String title; + + @Schema(description = "수정할 글의 내용" , example = "내용 수정 샘플") + private String content; + + @Schema(hidden = true) + private int viewCnt; + + @Schema(description = "표출 여부" , example = "Y") + private String expsrYn; + + @Schema(hidden = true) + private String delYn; + + @Schema(hidden = true) + private String createUserId; + + @Schema(hidden = true) + private Instant createDt; + + @Schema(description = "수정한 사람" , example = "palnet") + private String updateUserId; + + @Schema(description = "업데이트 일자") + private Instant updateDt; + +} diff --git a/web/api-cns/src/main/java/kr/co/palnet/kac/api/v1/cns/faq/service/CnsFaqService.java b/web/api-cns/src/main/java/kr/co/palnet/kac/api/v1/cns/faq/service/CnsFaqService.java new file mode 100644 index 0000000..ddccbc5 --- /dev/null +++ b/web/api-cns/src/main/java/kr/co/palnet/kac/api/v1/cns/faq/service/CnsFaqService.java @@ -0,0 +1,129 @@ +package kr.co.palnet.kac.api.v1.cns.faq.service; + +import com.palnet.biz.api.acnt.jwt.utils.JwtTokenUtil; +import com.palnet.biz.api.cns.faq.model.FaqListModel; +import com.palnet.biz.api.cns.faq.model.FaqListRQModel; +import com.palnet.biz.api.cns.faq.model.FaqListRSModel; +import com.palnet.biz.api.cns.faq.model.FaqUpdateModel; +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; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; +import java.util.ArrayList; +import java.util.List; + +@Slf4j +@Service +@RequiredArgsConstructor +public class CnsFaqService { + + private final CnsFaqQueryRepository query; + private final CnsFaqBasRepository cnsFaqBasRepository; + private final JwtTokenUtil jwtTokenUtil; + + /** + * FaQ 항목들 조회하는 기능. + * @param model + * @return + */ + public List getFaqList(FaqListRQModel model){ + + List result = query.getFaqList(model.getCategory(), model.getWord()); + + if(result == null) result = new ArrayList<>(); + + return result; + + } + + /** + * 일련번호[sno]로 상세정보를 조회하는 기능. + * @param sno + * @return + */ + public FaqListRSModel getFaqDetail(int sno){ + cnsFaqBasRepository.updateViewCnt(sno); + FaqListRSModel result = query.getFaqDetail(sno); + if(result == null) throw new CustomException(ErrorCode.DATA_NO); + return result; + } + + /** + * FaQ 추가하는 기능. + * @param model + * @return + */ + @Transactional + public boolean insertFaq(FaqListModel model){ + String userId = jwtTokenUtil.getUserIdByToken(); + + CnsFaqBas bas = new CnsFaqBas(); + + bas.setCreateUserId(userId); + bas.setUpdateUserId(userId); + + bas.setCategory(model.getCategory()); + bas.setContent(model.getContent()); + bas.setExpsrYn(model.getExpsrYn()); + bas.setTitle(model.getTitle()); + + bas.setDelYn("N"); + bas.setViewCnt(0); + + try { + CnsFaqBas saveEntity = cnsFaqBasRepository.save(bas); + if(saveEntity != null) return true; + else return false; + } catch(Exception e) { + // TODO parameter validate...?? + throw new CustomException(ErrorCode.NON_VALID_PARAMETER); + } + } + + /** + * FaQ 수정하는 기능. + * @param model + * @return + */ + @Transactional + public boolean updateFaq(FaqUpdateModel model) { + String userId = jwtTokenUtil.getUserIdByToken(); + + CnsFaqBas prevData = cnsFaqBasRepository.findById(model.getFaqSno()).orElseThrow(() -> 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()); + prevData.setUpdateUserId(userId); + + + try { + CnsFaqBas saveEntity = cnsFaqBasRepository.save(prevData); + if(saveEntity != null) return true; + else return false; + } catch(Exception e) { + // TODO parameter validate...?? + throw new CustomException(ErrorCode.NON_VALID_PARAMETER); + } + } + + /** + * 일련번호[sno]로 삭제하는 기능. + * @param sno + */ + public void deleteFaq(int sno) { + + // TODO 반환값이 실행한 갯수인지 확인 필요 + int delCnt = cnsFaqBasRepository.deleteFaq(sno); + + if(delCnt == 0) throw new CustomException(ErrorCode.DATA_NO); + + } +} \ No newline at end of file