thkim 2024-03-22 13:34:02 +09:00
commit 5b18827bb5
5 changed files with 184 additions and 51 deletions

View File

@ -4,7 +4,7 @@
NODE_PATH=src/
## 절대경로 지정
REACT_APP_EGOV_CONTEXT_URL=118.219.150.34:50688
REACT_APP_EGOV_CONTEXT_URL=back.kcsc.dbnt.co.kr
## [보안] 소스맵 삭제
GENERATE_SOURCEMAP=false

View File

@ -10,6 +10,7 @@ import {default as EgovLeftNav} from 'components/leftmenu/EgovLeftNavAdmin';
import EgovRadioButtonGroup from 'components/EgovRadioButtonGroup';
import {Form} from "react-bootstrap";
import RichTextEditor from "../../../components/editor/RichTextEditor";
import AttachFile from "../../../components/file/AttachFile";
function AdminPostMgtEdit({props, reloadFunction}) {
@ -34,27 +35,31 @@ function AdminPostMgtEdit({props, reloadFunction}) {
const [categoryList, setCategoryList] = useState([]);
const retrieveList = useCallback(() => {
const retrieveListURL = '/admin/boards/get-category-list';
const retrieveListURL = '/admin/boards/get-category-and-file-list';
const requestOptions = {
method: "GET",
method: "POST",
headers: {
'Content-type': 'application/json',
},
body: JSON.stringify()
body: JSON.stringify(item ? item : {})
}
EgovNet.requestFetch(retrieveListURL,
requestOptions,
(resp) => {
setCategoryList(resp.result.categoryList);
if (resp.result.files) {
setServerFiles(resp.result.files);
console.log("@@@ files : " + JSON.stringify(resp.result.files));
}
console.log("@@@ categoryList : " + JSON.stringify(resp.result.categoryList));
},
function (resp) {
console.log("err response : ", resp);
}
);
},[]);
}, []);
useEffect(() => {
retrieveList();
@ -65,33 +70,40 @@ function AdminPostMgtEdit({props, reloadFunction}) {
const initMode = () => {
if (modeInfo.mode === CODE.MODE_MODIFY) {
setBoardDetail(item);
setSelectedBbsSeq(props?.bbsSeq);
}
}
function editPost(e) {
const formData = new FormData();
e.preventDefault();
e.stopPropagation();
const form = e.target;
const info = {
fixedYn: defaultFixedYn,
secretYn: defaultSecretYn,
bbsId: form.bbsId.value,
bbsSeq: selectedBbsSeq,
bbsContTitle: form.bbsContTitle.value,
fileGrpId: form.fileGrpId.value,
bbsContents: text
formData.append('fixedYn', defaultFixedYn);
formData.append('secretYn', defaultSecretYn);
formData.append('bbsId', form.bbsId.value);
formData.append('bbsSeq', selectedBbsSeq);
formData.append('bbsContTitle', form.bbsContTitle.value);
formData.append('bbsContents', text);
//
if (files) {
for (let i = 0; i < files.length; i++) {
if (files[i].seq) {
// file seq .
formData.append("survivingFiles", files[i].seq);
continue;
}
formData.append("files", files[i]);
}
}
if (modeInfo.mode === CODE.MODE_MODIFY) {
info.bbsContSeq = props.bbsContSeq;
formData.append('bbsContSeq', props.bbsContSeq);
}
EgovNet.requestFetch(
'/admin/boards/post-mgt',
{
method: "PUT",
headers: {
'Content-type': 'application/json'
},
body: JSON.stringify(info)
body: formData
},
(resp) => {
if (Number(resp.resultCode) === Number(CODE.RCV_SUCCESS)) {
@ -106,8 +118,8 @@ function AdminPostMgtEdit({props, reloadFunction}) {
)
}
function deletePost(post){
if(window.confirm("삭제하시겠습니까?")) {
function deletePost(post) {
if (window.confirm("삭제하시겠습니까?")) {
EgovNet.requestFetch(
'/admin/boards/post-mgt',
{
@ -145,6 +157,28 @@ function AdminPostMgtEdit({props, reloadFunction}) {
setSelectedBbsSeq(selectedOption.bbsSeq);
}
const fileTypes = ["JPG", "PNG", "GIF", "PDF", "HWP", "HWPX", "ZIP", "JPEG", "MP4", "TXT"];
const [serverFiles, setServerFiles] = useState([]);
const [files, setFiles] = useState([]);
// file
const deleteFile = (fileSeq) => {
const deleteFileURL = `/contents/api/popup-manage/file/${fileSeq}`;
const requestOptions = {
method: "DELETE",
headers: {
'Content-type': 'application/json',
}
}
EgovNet.requestFetch(deleteFileURL,
requestOptions,
(resp) => {
}
);
}
return (
<>
<style>
@ -164,7 +198,9 @@ function AdminPostMgtEdit({props, reloadFunction}) {
<Modal.Body>
<div className="board_view2">
<Form onSubmit={(e) => {editPost(e)}} noValidate>
<Form onSubmit={(e) => {
editPost(e)
}} noValidate>
<dl>
<dt><label htmlFor="siteTitle">상단고정</label><span className="req">필수</span></dt>
<dd>
@ -208,14 +244,13 @@ function AdminPostMgtEdit({props, reloadFunction}) {
</dd>
</dl>
<dl>
<dt><label htmlFor="siteUrl">파일</label><span className="req">필수</span></dt>
<dt><label htmlFor="fileGrpId">파일</label><span className="req">필수</span></dt>
<dd>
<Form.Control className="f_input2 w_full" type="text" name="fileGrpId" placeholder="파일" required
defaultValue={props?.fileGrpId}/>
<AttachFile name="preDataFile" multiple={true} files={files} setFiles={setFiles} serverFiles={serverFiles} fileTypes={fileTypes} deleteFile={deleteFile}/>
</dd>
</dl>
<dl>
<dt><label htmlFor="fileGrpId">내용</label><span className="req">필수</span></dt>
<dt><label htmlFor="contents">내용</label><span className="req">필수</span></dt>
<dd>
<RichTextEditor item={text} setText={setText}/>
</dd>
@ -227,12 +262,17 @@ function AdminPostMgtEdit({props, reloadFunction}) {
<button type="submit" className="btn btn_skyblue_h46 w_100">저장
</button>
{modeInfo.mode === CODE.MODE_MODIFY &&
<button type={"button"} className="btn btn_skyblue_h46 w_100" onClick={()=>{deletePost(props)}}>삭제</button>
<button type={"button"} className="btn btn_skyblue_h46 w_100" onClick={() => {
deletePost(props)
}}>삭제</button>
}
</div>
<div className="right_col btn1">
<button type={"button"} className="btn btn_blue_h46 w_100" onClick={()=>{reloadFunction()}}>목록</button>
<button type={"button"} className="btn btn_blue_h46 w_100" onClick={() => {
reloadFunction()
}}>목록
</button>
</div>
</div>
{/* <!--// 버튼영역 --> */}

View File

@ -13,6 +13,7 @@ import AboutSiteModal from "../config/aboutSiteMgt/AboutSiteModal";
import AdminPostMgtEdit from "./AdminPostMgtEdit";
import Modal from "react-bootstrap/Modal";
import {format} from "date-fns";
import {Form} from "react-bootstrap";
function AdminPostMgtList(props) {
console.group("EgovAdminPostList");
@ -30,6 +31,7 @@ function AdminPostMgtList(props) {
const wrdRef = useRef();
const [listTag, setListTag] = useState([]);
const [categoryList, setCategoryList] = useState([]);
const [show, setShow] = useState(false);
const [modalBody, setModalBody] = useState();
@ -55,6 +57,7 @@ function AdminPostMgtList(props) {
requestOptions,
(resp) => {
setPaginationInfo(resp.result.paginationInfo);
setCategoryList(resp.result.categoryList);
let mutListTag = [];
listTag.push(<p className="no_data" key="0">검색된 결과가 없습니다.</p>); //
@ -92,6 +95,10 @@ function AdminPostMgtList(props) {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const handleSelectChange = (e) => {
}
function editPost(item){
handleShow();
if(item != undefined) {
@ -134,14 +141,12 @@ function AdminPostMgtList(props) {
<li className="third_1 L">
<span className="lb">검색유형선택</span>
<label className="f_select" htmlFor="searchCnd">
<select id="searchCnd" name="searchCnd" title="검색유형선택" ref={cndRef}
onChange={e => {
cndRef.current.value = e.target.value;
}}
>
<option value="0">게시판명</option>
<option value="1">게시판유형</option>
</select>
<Form.Select id="select1" name="bbsId" onChange={handleSelectChange}>
<option value="">선택</option>
{categoryList.map((item) => (
<option key={item.bbsSeq} value={item.bbsId} selected={props?.bbsId === item.bbsId}>{item.bbsTitle}</option>
))}
</Form.Select>
</label>
</li>
<li className="third_2 R">

View File

@ -11,6 +11,8 @@ 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 io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
@ -20,15 +22,15 @@ 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.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
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;
import java.util.List;
import java.util.Map;
@RestController
@ -39,6 +41,7 @@ public class AdminBoardsController extends BaseController {
private final AdminBoardsService adminBoardsService;
private final CommonCodeService commonCodeService;
private final FileService fileService;
/* ---- 게시판관리 ----- */
@Operation(
@ -152,27 +155,44 @@ public class AdminBoardsController extends BaseController {
public ResultVO getPostList() throws Exception {
ResultVO resultVO = new ResultVO();
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("categoryList", adminBoardsService.selectBoardList());
resultMap.put("postList", adminBoardsService.selectPostList());
resultVO.setResult(resultMap);
return resultVO;
}
@Operation(
summary = "게시물 카테고리 셀렉트박스 옵션 조회",
description = "게시물 카테고리 셀렉트박스 옵션 조회",
summary = "게시물 카테고리 셀렉트박스 옵션, 파일 목록 조회",
description = "게시물 카테고리 셀렉트박스 옵션, 파일 목록 조회",
tags = {"AdminBoardsController"}
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "조회 성공"),
@ApiResponse(responseCode = "403", description = "인가된 사용자가 아님")
})
@RequestMapping(method = RequestMethod.GET, value = "/get-category-list", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResultVO getCategoryList() throws Exception {
@RequestMapping(method = RequestMethod.POST, value = "/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;
}
@ -187,7 +207,13 @@ public class AdminBoardsController extends BaseController {
@ApiResponse(responseCode = "403", description = "인가된 사용자가 아님")
})
@RequestMapping(method = RequestMethod.PUT, value = "/post-mgt")
public ResultVO savePostMgt(@RequestBody @Valid TnBbsContents contents, HttpServletRequest request, Errors errors, @AuthenticationPrincipal LoginVO user) {
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());
@ -201,9 +227,9 @@ public class AdminBoardsController extends BaseController {
resultVO.setResultCode(ResponseCode.INPUT_CHECK_ERROR.getCode());
resultVO.setResultMessage(msg.toString());
} else {
System.out.println("@@@ contents.getBbsSeq() : " + contents.getBbsContSeq());
System.out.println("@@@ contents.getBbsSeq() : " + contents.getBbsSeq());
contents.setIpAddress(ClientUtils.getRemoteIP(request));
adminBoardsService.savePost(contents, user.getId());
adminBoardsService.savePost(contents, request, user, files, survivingFiles);
resultVO.setResultCode(ResponseCode.SUCCESS.getCode());
}
}

View File

@ -5,12 +5,19 @@ import com.dbnt.kcscbackend.admin.boards.entity.TnBbsContents;
import com.dbnt.kcscbackend.admin.boards.repository.TnBbsContentsRepository;
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.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.Date;
import java.util.List;
import java.util.Optional;
@ -20,6 +27,7 @@ public class AdminBoardsService extends EgovAbstractServiceImpl {
private final TnBbsRepository tnBbsRepository;
private final TnBbsContentsRepository tnBbsContentsRepository;
private final FileService fileService;
public List<TnBbs> selectBoardList() {
return tnBbsRepository.findAllByOrderByBbsSeqDesc();
@ -66,28 +74,82 @@ public class AdminBoardsService extends EgovAbstractServiceImpl {
return tnBbsContentsRepository.findAll();
}
@Transactional
public void savePost(TnBbsContents contents, String userId) {
/**
* .
* D:/kcscUploadFiles/XXXX/abc.jpg XXXX .
*
* @return
*/
private String getMiddlePath() {
//파일이 저장될 경로를 생성한다.
String domainPath = "post";
Date nowDate = new Date();
String strNowYyyy = new SimpleDateFormat("yyyy").format(nowDate);
String strNowYyyyMmdd = new SimpleDateFormat("yyyyMMdd").format(nowDate);
String middlePath = domainPath + "/" + strNowYyyy + "/" + strNowYyyyMmdd + "/";
return middlePath;
}
public void savePost(TnBbsContents contents, HttpServletRequest request, LoginVO user, MultipartFile[] files, long[] survivingFiles) throws Exception {
if (contents.getBbsContSeq() == null) {
String fileGrpId = fileService.addTnAttachFile(request, user, files, this.getMiddlePath());
// TODO 하드코딩
contents.setBbsContLevel(1L);
contents.setBbsReadCnt(0L);
contents.setUseYn("Y");
contents.setFrstCrtDt(LocalDateTime.now());
contents.setFrstCrtId(userId);
contents.setFrstCrtId(user.getId());
contents.setFileGrpId(fileGrpId);
tnBbsContentsRepository.save(contents);
} else {
TnBbsContents savedPost = tnBbsContentsRepository.findById(contents.getBbsContSeq()).orElse(null);
String fileGrpId = savedPost.getFileGrpId();
List<TnAttachFile> tnAttachFileList = fileService.findByFileGrpId(fileGrpId);
if (survivingFiles == null) {
//기존 file을 모두 삭제한다.
if (fileGrpId != null) {
if (tnAttachFileList != null) {
for (TnAttachFile item : tnAttachFileList) {
fileService.deleteTnAttachFile(request, user, item.getFileSeq().longValue());
}
}
fileGrpId = null;
}
} else {
// 살아남은 file을 제외한 나머지 file을 삭제한다.
if (tnAttachFileList != null) {
boolean isFound = false;
for (TnAttachFile item : tnAttachFileList) {
for (long oldFileSeq : survivingFiles) {
if (oldFileSeq == item.getFileSeq()) {
isFound = true;
break;
}
}
if (!isFound) {
fileService.deleteTnAttachFile(request, user, item.getFileSeq().longValue());
}
isFound = false;
}
}
}
System.out.println("@@@ files : " + files);
fileGrpId = fileService.addTnAttachFile(request, user, files, this.getMiddlePath(), fileGrpId);
savedPost.setFixedYn(contents.getFixedYn());
savedPost.setSecretYn(contents.getSecretYn());
savedPost.setBbsId(contents.getBbsId());
savedPost.setBbsSeq(contents.getBbsSeq());
savedPost.setBbsContTitle(contents.getBbsContTitle());
savedPost.setFileGrpId(contents.getFileGrpId());
savedPost.setBbsContents(contents.getBbsContents());
savedPost.setIpAddress(contents.getIpAddress());
savedPost.setUseYn("Y");
savedPost.setLastChgId(userId);
savedPost.setLastChgId(user.getId());
savedPost.setLastChgDt(LocalDateTime.now());
savedPost.setFileGrpId(fileGrpId);
tnBbsContentsRepository.save(savedPost);
}
}