댓글조회/등록

thkim
유민형 2024-07-18 17:53:10 +09:00
parent a53cc8237b
commit ff761d4385
8 changed files with 450 additions and 27 deletions

View File

@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, {useCallback, useEffect, useState} from 'react';
import {Link, useLocation, useNavigate, useParams} from 'react-router-dom';
import * as EgovNet from 'api/egovFetch';
@ -9,6 +9,7 @@ import Col from 'react-bootstrap/Col';
import Button from 'react-bootstrap/Button';
import {format} from "date-fns";
import DOMPurify from 'dompurify';
import CODE from "../../constants/code";
function Detail(){
@ -21,6 +22,9 @@ function Detail(){
const item = location.state?.item;
console.log("@@@ item : " + JSON.stringify(item));
const [listTag, setListTag] = useState([]);
const [comment, setComment] = useState('');
const goToList = () => {
navigate('/support/list/KCSC-QA');
};
@ -29,6 +33,86 @@ function Detail(){
navigate('/support/create/KCSC-QA');
};
const retrieveList = useCallback(() => {
const retrieveListURL = '/user/boards/reply/reply-list?bbsContSeq=' + bbsContSeq;
const requestOptions = {
method: "GET",
headers: {
'Content-type': 'application/json',
}
}
EgovNet.requestFetch(retrieveListURL,
requestOptions,
(resp) => {
let mutListTag = [];
setListTag([]);
resp.result.replyList.forEach(function (item) {
const finalModifiedDate = item?.lastChgDt ? item?.lastChgDt : item?.frstCrtDt;
const formattedDate = finalModifiedDate ? format(finalModifiedDate, "yyyy-MM-dd HH:mm:ss") : "";
mutListTag.push(
<li>
<span>{item?.frstCrtId}, {formattedDate}</span>
{item?.replCont}
<Link to="#" className="btn delete">삭제</Link>
</li>
);
});
if(!mutListTag.length) mutListTag.push(<p className="no_data" key="0">댓글이 없습니다.</p>); //
setListTag(mutListTag);
},
function (resp) {
console.log("err response : ", resp);
}
);
},[]);
useEffect(() => {
retrieveList();
}, []);
const handleChange = (event) => {
setComment(event.target.value);
};
const handleSubmit = (e) => {
console.log(comment);
editReply(e);
};
function editReply(e) {
e.preventDefault();
e.stopPropagation();
const data = {
bbsContSeq: bbsContSeq,
replCont: comment
};
console.log("@@@ data : " + JSON.stringify(data));
EgovNet.requestFetch(
'/user/boards/reply/reply-mgt',
{
method: "POST",
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
},
(resp) => {
if (Number(resp.resultCode) === Number(CODE.RCV_SUCCESS)) {
alert("저장되었습니다.");
setComment('');
retrieveList();
} else if (Number(resp.resultCode) === Number(CODE.RCV_ERROR_AUTH)) {
console.log("토큰 갱신중.")
} else {
alert(resp.result.resultMessage)
}
}
)
}
return (
<div className="container">
<div className="c_wrap">
@ -114,32 +198,18 @@ function Detail(){
</Col>
<Col xs={10} className="">
{/*<div>*/}
<textarea className="f_txtar w-100 h-100" name="" id="replay_write" rows="4"></textarea>
<textarea className="f_txtar w-100 h-100" name="" id="replay_write" rows="4"
value={comment} onChange={handleChange}></textarea>
{/*</div>*/}
</Col>
<Col xs={1} className="">
<Button type={"button"} className="btn btn-22498E w-100 h-100" >등록</Button>
<Button type={"button"} className="btn btn-22498E w-100 h-100" onClick={handleSubmit}>등록</Button>
</Col>
</Row>
{/* <!--// 답변달기 --> */}
<div className="qna_a">
<ul>
<li>
<span>chanjin, 2011-08-08 12:33:33</span>
심각: Servlet.service() for servlet action threw exception은 jsp파일을 열어서 보셔야합니다.<br />
javax.servlet.ServletException: Could not get RequestDispatcher for [/WEB-INF/jsp/egovframework//main/main.jsp]: check that<br />
<Link to="#" className="btn delete">삭제</Link>
</li>
<li>
<span>sunrise, 2011-08-07 11:11:11</span>
tomcat서버를 재시동해보세요. 그렇게 하니깐 되던데요.
<Link to="#" className="btn delete">삭제</Link>
</li>
<li>
<span>auto, 2011-08-07 11:11:11</span>
제가 살펴볼께요 메일로 주세요. test@naver.com
<Link to="#" className="btn delete">삭제</Link>
</li>
{listTag}
</ul>
</div>

View File

@ -42,9 +42,9 @@ function List(){
handleClose();
const params = EgovNet.convParams(searchCondition);
console.groupCollapsed("EgovAdminPostList.retrieveList()");
console.groupCollapsed("EgovUserPostList.retrieveList()");
const retrieveListURL = '/admin/boards/posts/post-list' + params;
const retrieveListURL = '/user/boards/posts/post-list' + params;
const requestOptions = {
method: "GET",
@ -84,7 +84,7 @@ function List(){
mutListTag.push(
<div className="list_item">
<div>{resp.result.resultCnt - (resp.result.paginationInfo.pageIndex -1) * resp.result.paginationInfo.rowCnt - index}</div>
<div className="al">{item?.bbsContTitle}</div>
<div className="al" onClick={()=>{Detail(item)}}>{item?.bbsContTitle}</div>
<div>{item?.frstCrtId}</div>
<div>{item?.bbsReadCnt}</div>
<div>{formattedDate}</div>
@ -99,7 +99,7 @@ function List(){
console.log("err response : ", resp);
}
);
console.groupEnd("EgovAdminPostList.retrieveList()");
console.groupEnd("EgovUserPostList.retrieveList()");
},[listTag]);
useEffect(() => {

View File

@ -2,10 +2,9 @@ package com.dbnt.kcscbackend.admin.boards;
import com.dbnt.kcscbackend.admin.boards.entity.TnBbs;
import com.dbnt.kcscbackend.admin.boards.entity.TnBbsContents;
import com.dbnt.kcscbackend.admin.boards.entity.TnBbsRepl;
import com.dbnt.kcscbackend.admin.boards.service.AdminBoardsService;
import com.dbnt.kcscbackend.admin.config.entity.TcMenu;
import com.dbnt.kcscbackend.auth.entity.LoginVO;
import com.dbnt.kcscbackend.commonCode.CommonCodeController;
import com.dbnt.kcscbackend.commonCode.service.CommonCodeService;
import com.dbnt.kcscbackend.config.common.BaseController;
import com.dbnt.kcscbackend.config.common.ResponseCode;
@ -26,7 +25,6 @@ import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.HashMap;

View File

@ -0,0 +1,57 @@
package com.dbnt.kcscbackend.admin.boards.entity;
import com.dbnt.kcscbackend.config.common.BoardParams;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import javax.persistence.*;
import java.time.LocalDateTime;
@Getter
@Setter
@Entity
@NoArgsConstructor
@DynamicInsert
@DynamicUpdate
@Table(name = "tn_bbs_repl")
public class TnBbsRepl extends BoardParams {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "repl_seq")
private Long replSeq;
@Column(name = "bbs_cont_seq", nullable = false)
private Long bbsContSeq;
@Column(name = "repl_cont", nullable = false, length = 550)
private String replCont;
@Column(name = "repl_cont_parent")
private Long replContParent;
@Column(name = "repl_cont_level", nullable = false)
private Long replContLevel;
@Column(name = "ip_address", nullable = false, length = 50)
private String ipAddress;
@Column(name = "frst_crt_id", nullable = false, length = 50)
private String frstCrtId;
@Column(name = "frst_crt_dt", nullable = false)
private LocalDateTime frstCrtDt;
@Column(name = "last_chg_id", length = 50)
private String lastChgId;
@Column(name = "last_chg_dt")
private LocalDateTime lastChgDt;
@Column(name = "use_yn", nullable = false, columnDefinition = "bpchar(1)")
private String useYn;
@Column(name = "old_seq")
private Long oldSeq;
}

View File

@ -0,0 +1,14 @@
package com.dbnt.kcscbackend.admin.boards.repository;
import com.dbnt.kcscbackend.admin.boards.entity.TnBbs;
import com.dbnt.kcscbackend.admin.boards.entity.TnBbsRepl;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;
public interface TnBbsReplRepository extends JpaRepository<TnBbsRepl, Long> {
List<TnBbsRepl> findByBbsContSeq(Long bbsContSeq);
Long countByBbsContSeq(Long bbsContSeq);
}

View File

@ -2,11 +2,13 @@ package com.dbnt.kcscbackend.admin.boards.service;
import com.dbnt.kcscbackend.admin.boards.entity.TnBbs;
import com.dbnt.kcscbackend.admin.boards.entity.TnBbsContents;
import com.dbnt.kcscbackend.admin.boards.entity.TnBbsRepl;
import com.dbnt.kcscbackend.admin.boards.mapper.AdminBoardsMapper;
import com.dbnt.kcscbackend.admin.boards.repository.TnBbsContentsRepository;
import com.dbnt.kcscbackend.admin.boards.repository.TnBbsReplRepository;
import com.dbnt.kcscbackend.admin.boards.repository.TnBbsRepository;
import com.dbnt.kcscbackend.admin.config.entity.TcMenu;
import com.dbnt.kcscbackend.auth.entity.LoginVO;
import com.dbnt.kcscbackend.config.util.ClientUtils;
import com.dbnt.kcscbackend.file.entity.TnAttachFile;
import com.dbnt.kcscbackend.file.service.FileService;
import lombok.RequiredArgsConstructor;
@ -26,6 +28,7 @@ public class AdminBoardsService extends EgovAbstractServiceImpl {
private final TnBbsRepository tnBbsRepository;
private final TnBbsContentsRepository tnBbsContentsRepository;
private final TnBbsReplRepository tnBbsReplRepository;
private final AdminBoardsMapper adminBoardsMapper;
private final FileService fileService;

View File

@ -0,0 +1,228 @@
package com.dbnt.kcscbackend.user;
import com.dbnt.kcscbackend.admin.boards.entity.TnBbsContents;
import com.dbnt.kcscbackend.admin.boards.entity.TnBbsRepl;
import com.dbnt.kcscbackend.admin.boards.service.AdminBoardsService;
import com.dbnt.kcscbackend.auth.entity.LoginVO;
import com.dbnt.kcscbackend.commonCode.service.CommonCodeService;
import com.dbnt.kcscbackend.config.common.BaseController;
import com.dbnt.kcscbackend.config.common.ResponseCode;
import com.dbnt.kcscbackend.config.common.ResultVO;
import com.dbnt.kcscbackend.config.util.ClientUtils;
import com.dbnt.kcscbackend.file.entity.TnAttachFile;
import com.dbnt.kcscbackend.file.service.FileService;
import com.dbnt.kcscbackend.user.service.UserBoardsService;
import io.swagger.v3.oas.annotations.Operation;
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 lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequiredArgsConstructor
@RequestMapping("/user/boards")
@Tag(name = "UserBoardsController", description = "사이트관리 게시판현황")
public class UserBoardsController extends BaseController {
private final AdminBoardsService adminBoardsService;
private final UserBoardsService userBoardsService;
private final CommonCodeService commonCodeService;
private final FileService fileService;
/* ---- 게시물관리 ----- */
@Operation(
summary = "게시물 목록 조회",
description = "게시물 목록 조회",
tags = {"UserBoardsController"}
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "조회 성공"),
@ApiResponse(responseCode = "403", description = "인가된 사용자가 아님")
})
@RequestMapping(method = RequestMethod.GET, value = "/posts/post-list", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResultVO getPostList(TnBbsContents tnBbsContents) throws Exception {
ResultVO resultVO = new ResultVO();
tnBbsContents.setQueryInfo();
Map<String, Object> resultMap = adminBoardsService.selectPostList(tnBbsContents);
resultMap.put("categoryList", adminBoardsService.selectBoardList());
int totCnt = Integer.parseInt((String)resultMap.get("resultCnt"));
tnBbsContents.setContentCnt(totCnt);
tnBbsContents.setPaginationInfo();
resultMap.put("paginationInfo", tnBbsContents);
resultVO.setResult(resultMap);
return resultVO;
}
@Operation(
summary = "게시물 카테고리 셀렉트박스 옵션, 파일 목록 조회",
description = "게시물 카테고리 셀렉트박스 옵션, 파일 목록 조회",
tags = {"UserBoardsController"}
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "조회 성공"),
@ApiResponse(responseCode = "403", description = "인가된 사용자가 아님")
})
@RequestMapping(method = RequestMethod.POST, value = "/posts/get-category-and-file-list", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResultVO getCategoryList(@RequestBody TnBbsContents tnBbsContents) throws Exception {
ResultVO resultVO = new ResultVO();
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("categoryList", adminBoardsService.selectBoardList());
//첨부파일명을 가져온다.
List<TnAttachFile> tnAttachFileList = fileService.findByFileGrpId(tnBbsContents.getFileGrpId());
System.out.println("@@@ fileGrpId : " + tnBbsContents.getFileGrpId());
if (tnAttachFileList != null) {
List<Map<String, Object>> files = new ArrayList<Map<String, Object>>();
for (TnAttachFile item : tnAttachFileList) {
Map<String, Object> fileDto = new HashMap<String, Object>();
fileDto.put("seq", item.getFileSeq());
fileDto.put("name", item.getFileOldName());
files.add(fileDto);
}
resultMap.put("files", files);
System.out.println("@@@ files : " + files);
} else {
resultMap.put("files", null);
}
resultVO.setResult(resultMap);
return resultVO;
}
@Operation(
summary = "게시물 저장",
description = "게시물 저장",
tags = {"UserBoardsController"}
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "저장 성공"),
@ApiResponse(responseCode = "403", description = "인가된 사용자가 아님")
})
@RequestMapping(method = RequestMethod.PUT, value = "/posts/post-mgt")
public ResultVO savePostMgt(
@Valid TnBbsContents contents,
HttpServletRequest request,
Errors errors,
@RequestParam(required = false) long[] survivingFiles,
@RequestParam(required = false) MultipartFile[] files,
@AuthenticationPrincipal LoginVO user) throws Exception {
ResultVO resultVO = new ResultVO();
if (user == null) {
resultVO.setResultCode(ResponseCode.TOKEN_EXPIRED.getCode());
} else {
if (errors.hasErrors()) {
StringBuilder msg = new StringBuilder();
for (FieldError error : errors.getFieldErrors()) {
msg.append(error.getDefaultMessage());
msg.append("\n");
}
resultVO.setResultCode(ResponseCode.INPUT_CHECK_ERROR.getCode());
resultVO.setResultMessage(msg.toString());
} else {
System.out.println("@@@ contents.getBbsSeq() : " + contents.getBbsSeq());
contents.setIpAddress(ClientUtils.getRemoteIP(request));
adminBoardsService.savePost(contents, request, user, files, survivingFiles);
resultVO.setResultCode(ResponseCode.SUCCESS.getCode());
}
}
return resultVO;
}
@Operation(
summary = "게시물 삭제",
description = "게시물 삭제",
tags = {"UserBoardsController"}
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "삭제 성공"),
@ApiResponse(responseCode = "403", description = "인가된 사용자가 아님")
})
@RequestMapping(method = RequestMethod.DELETE, value = "/posts/post-mgt")
public ResultVO deletePostMgt(@RequestBody TnBbsContents contents, HttpServletRequest request, @AuthenticationPrincipal LoginVO user) {
ResultVO resultVO = new ResultVO();
if (user == null) {
resultVO.setResultCode(ResponseCode.TOKEN_EXPIRED.getCode());
} else {
contents.setIpAddress(ClientUtils.getRemoteIP(request));
String result = adminBoardsService.deletePost(contents);
if (result == null) {
resultVO.setResultCode(ResponseCode.SUCCESS.getCode());
} else if (result.equals("notFind")) {
resultVO.setResultCode(ResponseCode.SAVE_ERROR.getCode());
resultVO.setResultMessage("대상이 존재하지 않습니다.");
}
}
return resultVO;
}
/* ---- 댓글관리 ----- */
@Operation(
summary = "댓글 목록 조회",
description = "댓글 목록 조회",
tags = {"UserBoardsController"}
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "조회 성공"),
@ApiResponse(responseCode = "403", description = "인가된 사용자가 아님")
})
@RequestMapping(method = RequestMethod.GET, value = "/reply/reply-list", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResultVO getReplyList(TnBbsRepl tnBbsRepl) throws Exception {
ResultVO resultVO = new ResultVO();
Map<String, Object> resultMap = userBoardsService.selectReplyList(tnBbsRepl.getBbsContSeq());
resultVO.setResult(resultMap);
return resultVO;
}
@Operation(
summary = "댓글 저장",
description = "댓글 저장",
tags = {"UserBoardsController"}
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "저장 성공"),
@ApiResponse(responseCode = "403", description = "인가된 사용자가 아님")
})
@RequestMapping(method = RequestMethod.POST, value = "/reply/reply-mgt")
public ResultVO saveReplyMgt(
@Valid @RequestBody TnBbsRepl tnBbsRepl,
HttpServletRequest request,
Errors errors,
@AuthenticationPrincipal LoginVO user) throws Exception {
System.out.println("@@@ : " + tnBbsRepl.getBbsContSeq());
System.out.println("@@@ : " + tnBbsRepl.getReplCont());
ResultVO resultVO = new ResultVO();
if (user == null) {
resultVO.setResultCode(ResponseCode.TOKEN_EXPIRED.getCode());
} else {
if (errors.hasErrors()) {
StringBuilder msg = new StringBuilder();
for (FieldError error : errors.getFieldErrors()) {
msg.append(error.getDefaultMessage());
msg.append("\n");
}
resultVO.setResultCode(ResponseCode.INPUT_CHECK_ERROR.getCode());
resultVO.setResultMessage(msg.toString());
} else {
System.out.println("@@@ tnBbsRepl.getBbsSeq() : " + tnBbsRepl.getBbsContSeq());
userBoardsService.saveReply(tnBbsRepl, request, user);
resultVO.setResultCode(ResponseCode.SUCCESS.getCode());
}
}
return resultVO;
}
}

View File

@ -0,0 +1,53 @@
package com.dbnt.kcscbackend.user.service;
import com.dbnt.kcscbackend.admin.boards.entity.TnBbs;
import com.dbnt.kcscbackend.admin.boards.entity.TnBbsContents;
import com.dbnt.kcscbackend.admin.boards.entity.TnBbsRepl;
import com.dbnt.kcscbackend.admin.boards.mapper.AdminBoardsMapper;
import com.dbnt.kcscbackend.admin.boards.repository.TnBbsContentsRepository;
import com.dbnt.kcscbackend.admin.boards.repository.TnBbsReplRepository;
import com.dbnt.kcscbackend.admin.boards.repository.TnBbsRepository;
import com.dbnt.kcscbackend.auth.entity.LoginVO;
import com.dbnt.kcscbackend.config.util.ClientUtils;
import com.dbnt.kcscbackend.file.entity.TnAttachFile;
import com.dbnt.kcscbackend.file.service.FileService;
import lombok.RequiredArgsConstructor;
import org.egovframe.rte.fdl.cmmn.EgovAbstractServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;
@Service
@RequiredArgsConstructor
public class UserBoardsService extends EgovAbstractServiceImpl {
private final TnBbsRepository tnBbsRepository;
private final TnBbsContentsRepository tnBbsContentsRepository;
private final TnBbsReplRepository tnBbsReplRepository;
private final AdminBoardsMapper adminBoardsMapper;
private final FileService fileService;
public Map<String, Object> selectReplyList(Long bbsContSeq) {
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("replyList", tnBbsReplRepository.findByBbsContSeq(bbsContSeq));
resultMap.put("resultCnt", tnBbsReplRepository.countByBbsContSeq(bbsContSeq));
return resultMap;
}
public void saveReply(TnBbsRepl tnBbsRepl, HttpServletRequest request, LoginVO user) throws Exception {
tnBbsRepl.setBbsContSeq(tnBbsRepl.getBbsContSeq());
tnBbsRepl.setReplCont(tnBbsRepl.getReplCont());
tnBbsRepl.setReplContLevel(1L);
tnBbsRepl.setIpAddress(ClientUtils.getRemoteIP(request));
tnBbsRepl.setFrstCrtId(user.getId());
tnBbsRepl.setFrstCrtDt(LocalDateTime.now());
tnBbsRepl.setUseYn("Y");
tnBbsReplRepository.save(tnBbsRepl);
}
}