에디터 첨부 이미지 게시글 번호 업데이트 로직 추가

This commit is contained in:
nevermoregb 2025-03-20 16:16:52 +09:00
parent f37a281f6c
commit 302efa298c
7 changed files with 243 additions and 19 deletions

View File

@ -131,6 +131,13 @@ public class BoardController {
return ApiResponse.ok(board); return ApiResponse.ok(board);
} }
/**
* 게시물 수정 조회(익명 게시글은 비밀번호 필수)
*
* @param boardId
* @param map
* @return
*/
@Member @Member
@ParameterCheck @ParameterCheck
@PostMapping("/{boardId}") @PostMapping("/{boardId}")
@ -176,8 +183,7 @@ public class BoardController {
@ParameterCheck @ParameterCheck
@DeleteMapping("/{boardId}") @DeleteMapping("/{boardId}")
public ApiResponse<String> deleteBoard(@ReqMap MapDto map) { public ApiResponse<String> deleteBoard(@ReqMap MapDto map) {
boardService.deleteBoard(map); return boardService.deleteBoard(map);
return ApiResponse.ok("게시물이 삭제되었습니다.");
} }

View File

@ -18,10 +18,12 @@ import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.List;
import java.util.UUID; import java.util.UUID;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
@ -29,8 +31,11 @@ import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import io.company.localhost.common.annotation.ParameterCheck; import io.company.localhost.common.annotation.ParameterCheck;
import io.company.localhost.common.annotation.ReqMap;
import io.company.localhost.common.dto.ApiResponse; import io.company.localhost.common.dto.ApiResponse;
import io.company.localhost.common.dto.MapDto;
import io.company.localhost.service.localbordService;
import io.company.localhost.utils.AuthUtil;
import io.company.localhost.vo.UploadFile;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -43,6 +48,8 @@ public class ImageUploadController {
@Value("${filePath.boardfile}") @Value("${filePath.boardfile}")
private String boardFilePath; private String boardFilePath;
private final localbordService localbordService;
/** /**
* quilleditor 안에서 삽입된 이미지를 서버에 저장하는 메소드 * quilleditor 안에서 삽입된 이미지를 서버에 저장하는 메소드
* @form-data 서버에 저장된 이미지 경로와 이름 * @form-data 서버에 저장된 이미지 경로와 이름
@ -50,7 +57,8 @@ public class ImageUploadController {
*/ */
@ParameterCheck @ParameterCheck
@PostMapping("/upload") @PostMapping("/upload")
public ApiResponse<String> uploadImage(@RequestParam("file") MultipartFile file) throws IOException { @Transactional
public ApiResponse<MapDto> uploadImage(@RequestParam("file") MultipartFile file) throws IOException {
if (file.isEmpty()) { if (file.isEmpty()) {
return ApiResponse.error(HttpStatus.BAD_REQUEST, "File is empty"); return ApiResponse.error(HttpStatus.BAD_REQUEST, "File is empty");
@ -59,13 +67,47 @@ public class ImageUploadController {
String fileExtension = originalFileName.substring(originalFileName.lastIndexOf(".")); String fileExtension = originalFileName.substring(originalFileName.lastIndexOf("."));
String fileName = UUID.randomUUID().toString() + fileExtension; String fileName = UUID.randomUUID().toString() + fileExtension;
Path filePath = Paths.get(boardFilePath, fileName); Path filePath = Paths.get(boardFilePath, fileName);
Long fileSize = file.getSize();
Files.createDirectories(filePath.getParent()); Files.createDirectories(filePath.getParent());
Files.write(filePath, file.getBytes()); Files.write(filePath, file.getBytes());
String fileUrl = "upload/img/board/" + fileName; String fileUrl = "upload/img/board/" + fileName;
long fileIndex = insertUploadEditorImageInfo(fileName, originalFileName, fileUrl, fileExtension, fileSize);
return ApiResponse.ok(fileUrl); MapDto map = new MapDto();
if(fileIndex != 0) map.put("fileIndex", fileIndex);
map.put("fileUrl", fileUrl);
return ApiResponse.ok(map);
}
/**
* 업로드 파일정보를 DB 적재
*
* @param fileName
* @param originalFileName
* @param filePath
* @param fileExtension
* @param fileSize
* @return success: DB 시퀀스 번호, fail: 0
*
*/
public long insertUploadEditorImageInfo(String fileName, String originalFileName, String filePath, String fileExtension, Long fileSize) {
Long userId = AuthUtil.getUser().getId();
MapDto map = new MapDto();
map.put("CMNFLENAM" , fileName );
map.put("CMNFLEORG" , originalFileName );
map.put("CMNFLEPAT" , filePath );
map.put("CMNFLEEXT" , fileExtension );
map.put("CMNFLESIZ" , fileSize );
map.put("CMNFLEREG" , userId );
int result = localbordService.insertUploadEditorImageInfo(map);
return result == 1 ? map.getLong("id") : 0;
} }
} }

View File

@ -15,8 +15,11 @@
package io.company.localhost.mapper; package io.company.localhost.mapper;
import java.util.List; import java.util.List;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import io.company.localhost.common.dto.MapDto; import io.company.localhost.common.dto.MapDto;
import io.company.localhost.vo.FileVo;
@Mapper @Mapper
public interface localbordMapper { public interface localbordMapper {
@ -111,6 +114,16 @@ public interface localbordMapper {
MapDto selectBoardDetail2(Long boardId); MapDto selectBoardDetail2(Long boardId);
int insertUploadEditorImageInfo(MapDto map);
int updateBoardIndexToFile(MapDto map);
List<FileVo> selectFilesInfo(MapDto map);
void deleteFiles(MapDto map);
void deleteGoodOrBadByBoardId(MapDto map);
} }

View File

@ -139,4 +139,14 @@ public class FileService {
public boolean removeFile(String path, String fileName) { public boolean removeFile(String path, String fileName) {
return fileUtil.removeFile(path, fileName); return fileUtil.removeFile(path, fileName);
} }
/**
* 게시글 파일 삭제
*
* @param fileName
* @return
*/
public boolean removeBoardFile(String fileName) {
return fileUtil.removeFile(boardFilePath, fileName);
}
} }

View File

@ -38,6 +38,7 @@ import io.company.localhost.mapper.localbordMapper;
import io.company.localhost.utils.AuthUtil; import io.company.localhost.utils.AuthUtil;
import io.company.localhost.utils.BlobUtil; import io.company.localhost.utils.BlobUtil;
import io.company.localhost.utils.PageUtil; import io.company.localhost.utils.PageUtil;
import io.company.localhost.vo.FileVo;
import io.company.localhost.vo.MemberVo; import io.company.localhost.vo.MemberVo;
import io.company.localhost.vo.UploadFile; import io.company.localhost.vo.UploadFile;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@ -80,6 +81,7 @@ public class localbordService {
boardMapper.updateIncrementViewCount(boardId); boardMapper.updateIncrementViewCount(boardId);
} }
@SuppressWarnings("unchecked")
public BigInteger insertBoard(MapDto map) { public BigInteger insertBoard(MapDto map) {
// 익명게시판이면 회원 정보를 null로 설정 // 익명게시판이면 회원 정보를 null로 설정
if ("300102".equals(String.valueOf(map.get("LOCBRDTYP")))) { if ("300102".equals(String.valueOf(map.get("LOCBRDTYP")))) {
@ -89,6 +91,14 @@ public class localbordService {
map.put("MEMBERSEQ", userId); map.put("MEMBERSEQ", userId);
} }
boardMapper.insertBoard(map); boardMapper.insertBoard(map);
//에디터 첨부 이미지 게시글 번호 업데이트
if(map.get("editorUploadedImgList") != null) {
ArrayList<String> editorUploadedImgList = (ArrayList<String>) map.get("editorUploadedImgList");
map.put("editorImgList", editorUploadedImgList);
this.updateBoardIndexToFile(map);
}
return (BigInteger) map.get("LOCBRDSEQ"); return (BigInteger) map.get("LOCBRDSEQ");
} }
@ -126,9 +136,47 @@ public class localbordService {
return boardMapper.selectAttachments(boardId); return boardMapper.selectAttachments(boardId);
} }
public void deleteBoard(MapDto map) { @Transactional
boardMapper.deleteCommentsByBoardId(map); public ApiResponse<String> deleteBoard(MapDto map) {
boardMapper.deleteBoard(map); boardMapper.deleteCommentsByBoardId(map); // 댓글 대댓글
boardMapper.deleteGoodOrBadByBoardId(map); // 좋아요 실어요 삭제
this.deleteBoardFiles(map); // 파일 삭제
boardMapper.deleteBoard(map); // 게시글 삭제
return ApiResponse.ok("게시물이 삭제되었습니다.");
}
/**
* 게시글 첨부파일 에디터 이미지 파일 삭제
*
* @param map
*/
private void deleteBoardFiles(MapDto map) {
List<FileVo> list = this.selectFilesInfo(map); // 삭제 파일 정보 조회
for(FileVo vo : list) {
String fileName = vo.getCMNFLENAM();
if(!fileName.contains(vo.getCMNFLEEXT())) fileName = fileName + vo.getCMNFLEEXT();
fileService.removeBoardFile(fileName); // 파일 삭제
}
this.deleteFiles(map); // 파일 데이터 삭제
}
/**
* 게시글 파일정보 삭제
* @param map
*/
public void deleteFiles(MapDto map) {
boardMapper.deleteFiles(map);
}
/**
* 게시글 파일 정보 조회
*
* @param map
* @return
*/
public List<FileVo> selectFilesInfo(MapDto map) {
return boardMapper.selectFilesInfo(map);
} }
public int updateBoard(MapDto map) { public int updateBoard(MapDto map) {
@ -345,18 +393,29 @@ public class localbordService {
public ApiResponse<String> updateBoardWithFiles(MapDto map, List<MultipartFile> files) throws IOException { public ApiResponse<String> updateBoardWithFiles(MapDto map, List<MultipartFile> files) throws IOException {
int result = this.updateBoard(map); // 게시글 수정 int result = this.updateBoard(map); // 게시글 수정
Long userId = AuthUtil.getUser().getId(); Long userId = AuthUtil.getUser().getId();
String[] editorUploadedImgList = null;
// 수정 성공 첨부파일 수정 변경
if(result == 1) { if(result == 1) {
//에디터 첨부 이미지 게시글 번호 업데이트
if(map.get("editorUploadedImgList") != null) {
editorUploadedImgList = String.valueOf(map.get("editorUploadedImgList")).split(",");
map.put("editorImgList", editorUploadedImgList);
this.updateBoardIndexToFile(map);
}
// 추가 첨부파일 업로드
if(files != null && !files.isEmpty()) { if(files != null && !files.isEmpty()) {
List<UploadFile> list = fileService.boardUploadFiles(files); // 파일 업로드 List<UploadFile> list = fileService.boardUploadFiles(files); // 파일 업로드
map.put("CMNFLEREG", userId); map.put("CMNFLEREG", userId);
map.put("list", list); map.put("list", list);
boardMapper.insertAttachments(map); boardMapper.insertAttachments(map); // 파일 정보 DB 적재
} }
// 제거 첨부파일 삭제
if(map.get("delFileIdx") != null) { if(map.get("delFileIdx") != null) {
String[] array = String.valueOf(map.get("delFileIdx")).split(","); String[] array = String.valueOf(map.get("delFileIdx")).split(",");
List<String> delListInfo = this.selectDelFileInfo(array); // 삭제 정보 조회 List<String> delListInfo = this.selectDelFileInfo(array); // 삭제 파일 정보 조회
for(String item : delListInfo) { for(String item : delListInfo) {
fileService.removeFile(item); // 파일 삭제 fileService.removeFile(item); // 파일 삭제
} }
@ -378,6 +437,10 @@ public class localbordService {
boardMapper.deleteFileInfo(array); boardMapper.deleteFileInfo(array);
} }
private int updateBoardIndexToFile(MapDto map) {
return boardMapper.updateBoardIndexToFile(map);
}
/** /**
* 삭제 첨부파일 정보 조회 * 삭제 첨부파일 정보 조회
* *
@ -427,12 +490,15 @@ public class localbordService {
return ApiResponse.error(HttpStatus.NOT_FOUND, "해당 게시글이 없습니다"); return ApiResponse.error(HttpStatus.NOT_FOUND, "해당 게시글이 없습니다");
} }
this.enrichBoardDetail(resultMap); // 추가정보 this.enrichBoardDetail(resultMap); // 추가정보 세팅
// 📌 첨부파일 목록 추가 List<MapDto> attachments = this.selectAttachments(boardId); // 📌 첨부파일 목록 추가
List<MapDto> attachments = this.selectAttachments(boardId);
resultMap.put("attachments", attachments != null ? attachments : new ArrayList<>()); resultMap.put("attachments", attachments != null ? attachments : new ArrayList<>());
return ApiResponse.ok(resultMap); return ApiResponse.ok(resultMap);
} }
public int insertUploadEditorImageInfo(MapDto map) {
return boardMapper.insertUploadEditorImageInfo(map);
}
} }

View File

@ -0,0 +1,18 @@
package io.company.localhost.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class FileVo {
private String CMNFLESEQ;
private String CMNBRDSEQ;
private String CMNFLENAM;
private String CMNFLEORG;
private String CMNFLEPAT;
private String CMNFLEEXT;
private String CMNFLESIZ;
private String CMNFLETYP;
}

View File

@ -97,6 +97,32 @@
</foreach> </foreach>
</insert> </insert>
<!-- 에디터 첨부 이미지 저장 -->
<insert id="insertUploadEditorImageInfo" parameterType="map">
<selectKey keyProperty="id" order="AFTER" resultType="long">
SELECT LAST_INSERT_ID()
</selectKey>
INSERT INTO commonfil (
CMNFLENAM,
CMNFLEORG,
CMNFLEPAT,
CMNFLEEXT,
CMNFLESIZ,
CMNFLEREG,
CMNFLERDT,
CMNFLETYP
) VALUES (
#{CMNFLENAM},
#{CMNFLEORG},
#{CMNFLEPAT},
#{CMNFLEEXT},
#{CMNFLESIZ},
#{CMNFLEREG},
NOW(),
2
)
</insert>
<!-- 게시물 상세정보 조회 --> <!-- 게시물 상세정보 조회 -->
<select id="selectBoardDetail" resultType="io.company.localhost.common.dto.MapDto"> <select id="selectBoardDetail" resultType="io.company.localhost.common.dto.MapDto">
@ -139,10 +165,35 @@
SELECT CMNFLESEQ AS id, CMNFLEORG AS originalName, CMNFLENAM AS fileName, CMNFLEPAT AS path, SELECT CMNFLESEQ AS id, CMNFLEORG AS originalName, CMNFLENAM AS fileName, CMNFLEPAT AS path,
CMNFLEEXT AS extension, CMNFLESIZ AS size, CMNFLERDT AS uploadDate CMNFLEEXT AS extension, CMNFLESIZ AS size, CMNFLERDT AS uploadDate
FROM commonfil FROM commonfil
WHERE CMNBRDSEQ = #{boardId} WHERE CMNBRDSEQ = #{boardId} and CMNFLETYP = 1
ORDER BY CMNFLERDT DESC ORDER BY CMNFLERDT DESC
</select> </select>
<!-- 파일 목록 조회 -->
<select id="selectFilesInfo" resultType="io.company.localhost.vo.FileVo">
SELECT
CMNFLESEQ,
CMNBRDSEQ,
CMNFLENAM,
CMNFLEORG,
CMNFLEPAT,
CMNFLEEXT,
CMNFLESIZ,
CMNFLETYP
FROM
COMMONFIL
WHERE
CMNBRDSEQ = #{LOCBRDSEQ}
</select>
<!-- 파일 목록 조회 -->
<select id="deleteFiles">
DELETE FROM
COMMONFIL
WHERE
CMNBRDSEQ = #{LOCBRDSEQ}
</select>
<!-- 게시물 삭제 --> <!-- 게시물 삭제 -->
<delete id="deleteBoard"> <delete id="deleteBoard">
DELETE FROM localbord WHERE LOCBRDSEQ = #{LOCBRDSEQ} DELETE FROM localbord WHERE LOCBRDSEQ = #{LOCBRDSEQ}
@ -154,6 +205,12 @@
WHERE LOCBRDSEQ = #{LOCBRDSEQ} WHERE LOCBRDSEQ = #{LOCBRDSEQ}
</delete> </delete>
<!-- 게시물 삭제 시 좋아요/싫어요 삭제 -->
<delete id="deleteGoodOrBadByBoardId">
DELETE FROM localgorb
WHERE LOCBRDSEQ = #{LOCBRDSEQ}
</delete>
<!-- 게시물 수정 --> <!-- 게시물 수정 -->
<update id="updateBoard"> <update id="updateBoard">
UPDATE localbord UPDATE localbord
@ -179,6 +236,18 @@
AND MEMBERSEQ = #{MEMBERSEQ} AND MEMBERSEQ = #{MEMBERSEQ}
</update> </update>
<update id="updateBoardIndexToFile">
UPDATE
COMMONFIL
SET
CMNBRDSEQ = #{LOCBRDSEQ}
WHERE
CMNFLESEQ IN
<foreach collection="editorImgList" item="item" separator="," open="(" close=")">
#{item}
</foreach>
</update>
<!-- 새 반응 삽입 --> <!-- 새 반응 삽입 -->
<insert id="insertReaction"> <insert id="insertReaction">
INSERT INTO localgorb (LOCBRDSEQ, LOCCMTSEQ, MEMBERSEQ, LOCGOBGOD, LOCGOBBAD) INSERT INTO localgorb (LOCBRDSEQ, LOCCMTSEQ, MEMBERSEQ, LOCGOBGOD, LOCGOBBAD)
@ -285,7 +354,7 @@
<select id="selectIsAttachments" resultType="int"> <select id="selectIsAttachments" resultType="int">
SELECT COUNT(*) SELECT COUNT(*)
FROM commonfil FROM commonfil
WHERE CMNBRDSEQ = #{boardId} WHERE CMNBRDSEQ = #{boardId} and CMNFLETYP = 1
</select> </select>
<!-- 게시물 좋아요/싫어요 개수 조회 --> <!-- 게시물 좋아요/싫어요 개수 조회 -->