Merge branch 'main' into yoon

This commit is contained in:
yoon 2025-03-07 11:14:06 +09:00
commit 7091e41a18
7 changed files with 332 additions and 16 deletions

View File

@ -15,14 +15,12 @@
package io.company.localhost.controller.api; package io.company.localhost.controller.api;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.math.BigInteger; import java.math.BigInteger;
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.ArrayList; import java.util.ArrayList;
import java.util.Base64;
import java.util.List; import java.util.List;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
@ -31,7 +29,15 @@ import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import com.github.pagehelper.PageInfo; import com.github.pagehelper.PageInfo;
@ -165,18 +171,21 @@ public class BoardController {
return ApiResponse.ok("게시물이 삭제되었습니다."); return ApiResponse.ok("게시물이 삭제되었습니다.");
} }
/** /**
* 게시물 수정 * 게시물 수정
* @ReqMap map 수정 데이터 (LOCBRDTTL, LOCBRDCON, LOCBRDSEQ) *
* @return 수정 결과 메시지 * @param map 수정 데이터 (LOCBRDTTL, LOCBRDCON, LOCBRDSEQ, delListInfo)
* @param files 첨부파일 정보
* @return
* @throws IOException
*/ */
@Member @Member
@ParameterCheck
@PutMapping("/{boardId}") @PutMapping("/{boardId}")
public ApiResponse<String> updateBoard(@ReqMap MapDto map) { public ApiResponse<String> updateBoard(@ReqMap MapDto map, @RequestPart(value = "files", required = false) List<MultipartFile> files) throws IOException {
boardService.updateBoard(map); return boardService.updateBoardWithFiles(map, files);
return ApiResponse.ok("게시물이 수정되었습니다.");
} }
/** /**
* 첨부파일 추가 * 첨부파일 추가

View File

@ -42,7 +42,7 @@ public interface localbordMapper {
void deleteCommentsByBoardId(MapDto map); void deleteCommentsByBoardId(MapDto map);
// 게시물 수정 // 게시물 수정
void updateBoard(MapDto map); int updateBoard(MapDto map);
// 기존 반응 조회 // 기존 반응 조회
MapDto selectReaction(MapDto map); MapDto selectReaction(MapDto map);
@ -100,6 +100,12 @@ public interface localbordMapper {
//댓글id 확인 //댓글id 확인
MapDto selectCommentById(int commentId); MapDto selectCommentById(int commentId);
void insertAttachments(MapDto map);
List<String> selectDelFileInfo(String[] array);
void deleteFileInfo(String[] array);
} }

View File

@ -19,11 +19,15 @@ 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.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
import java.util.List;
import java.util.UUID; import java.util.UUID;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import io.company.localhost.utils.FileUtil;
import io.company.localhost.vo.UploadFile;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@Service @Service
@ -37,6 +41,8 @@ public class FileService {
@Value("${filePath.boardfile}") @Value("${filePath.boardfile}")
private String boardFilePath; private String boardFilePath;
private final FileUtil fileUtil;
/** /**
* 파일 업로드 * 파일 업로드
* *
@ -101,4 +107,36 @@ public class FileService {
throw new RuntimeException("파일 업로드 실패: " + e.getMessage()); throw new RuntimeException("파일 업로드 실패: " + e.getMessage());
} }
} }
/**
* 게시판 다중파일 업로드
*
* @param files
* @return
* @throws IOException
*/
public List<UploadFile> boardUploadFiles(List<MultipartFile> files) throws IOException {
return fileUtil.uploadFiles(boardFilePath, files);
}
/**
* 파일 삭제
*
* @param path 경로+파일명
* @return
*/
public boolean removeFile(String path) {
return fileUtil.removeFile(path);
}
/**
* 파일 삭제
*
* @param path
* @param fileName
* @return
*/
public boolean removeFile(String path, String fileName) {
return fileUtil.removeFile(path, fileName);
}
} }

View File

@ -14,14 +14,11 @@
*************************************************************/ *************************************************************/
package io.company.localhost.service; package io.company.localhost.service;
import java.io.IOException;
import java.math.BigInteger; import java.math.BigInteger;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import io.company.localhost.utils.BlobUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
@ -31,10 +28,13 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo; import com.github.pagehelper.PageInfo;
import io.company.localhost.common.dto.ApiResponse;
import io.company.localhost.common.dto.MapDto; import io.company.localhost.common.dto.MapDto;
import io.company.localhost.mapper.localbordMapper; 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.PageUtil; import io.company.localhost.utils.PageUtil;
import io.company.localhost.vo.UploadFile;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@Service @Service
@ -123,8 +123,8 @@ public class localbordService {
boardMapper.deleteBoard(map); boardMapper.deleteBoard(map);
} }
public void updateBoard(MapDto map) { public int updateBoard(MapDto map) {
boardMapper.updateBoard(map); return boardMapper.updateBoard(map);
} }
public void procReactToBoard(MapDto map) { public void procReactToBoard(MapDto map) {
@ -338,4 +338,58 @@ public class localbordService {
} }
} }
/**
* 게시판 수정
*
* @param map
* @param files
* @throws IOException
*/
@Transactional
public ApiResponse<String> updateBoardWithFiles(MapDto map, List<MultipartFile> files) throws IOException {
int result = this.updateBoard(map); // 게시글 수정
Long userId = AuthUtil.getUser().getId();
if(result == 1) {
if(files != null && !files.isEmpty()) {
List<UploadFile> list = fileService.boardUploadFiles(files); // 파일 업로드
map.put("CMNFLEREG", userId);
map.put("list", list);
boardMapper.insertAttachments(map);
}
if(map.get("delFileIdx") != null) {
String[] array = String.valueOf(map.get("delFileIdx")).split(",");
List<String> delListInfo = this.selectDelFileInfo(array); // 삭제 정보 조회
for(String item : delListInfo) {
fileService.removeFile(item); // 파일 삭제
}
this.deleteFileInfo(array); // db 데이터 삭제
}
} else {
return ApiResponse.ok("게시물이 수정에 실패하였습니다.");
}
return ApiResponse.ok("게시물이 수정되었습니다.");
}
/**
* 첨부파일 데이터 삭제
*
* @param array
*/
private void deleteFileInfo(String[] array) {
boardMapper.deleteFileInfo(array);
}
/**
* 삭제 첨부파일 정보 조회
*
* @param array
* @return
*/
private List<String> selectDelFileInfo(String[] array) {
return boardMapper.selectDelFileInfo(array);
}
} }

View File

@ -0,0 +1,147 @@
package io.company.localhost.utils;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import io.company.localhost.vo.UploadFile;
import lombok.extern.slf4j.Slf4j;
@Component
@Slf4j
public class FileUtil {
/**
* 파일 저장 경로/파일명
* @param savePath
* @param fileName
* @return
*/
public String getFullPath(String savePath, String fileName) {
return Paths.get(savePath, fileName).toString();
}
/**
* DB에 저장될 파일명
* @param originalFilename
* @return
*/
public String createSaveFileName(String originalFilename) {
String uuid = UUID.randomUUID().toString();
String extension = getFileExtension(originalFilename);
return uuid + "." + extension;
}
/**
* 파일 확장자
* @param originalFileName
* @return
*/
public String getFileExtension(String originalFileName) {
int pos = originalFileName.lastIndexOf(".");
return originalFileName.substring(pos + 1);
}
/**
* 파일 확장자 제외
* @param
* @return
*/
public String excludeFileExtension(String withExt) {
int pos = withExt.lastIndexOf(".");
return withExt.substring(0, pos);
}
/**
* 단일 파일 업로드
* @param savePath
* @param multipartFile
* @return
* @throws IOException
*/
public UploadFile uploadFile(String savePath, MultipartFile multipartFile) throws IOException{
File saveDirectory = new File(savePath);
if(!saveDirectory.exists()) saveDirectory.mkdirs();
if(multipartFile.isEmpty()) return null;
String originalFileName = multipartFile.getOriginalFilename(); //원본파일명
String saveFileName = createSaveFileName(originalFileName); //난수명
String ext = getFileExtension(originalFileName); //확장자
Long size = multipartFile.getSize(); //사이즈
String filePath = getFullPath(savePath, saveFileName);
multipartFile.transferTo(new File(filePath));
String orgFileName = this.excludeFileExtension(originalFileName);
String savFileName = this.excludeFileExtension(saveFileName);
return new UploadFile(orgFileName, savFileName, ext, size, filePath);
}
/**
* 다중 파일 업로드
* @param savePath
* @param multipartFiles
* @return
* @throws IOException
*/
public List<UploadFile> uploadFiles(String savePath, List<MultipartFile> multipartFiles) throws IOException{
List<UploadFile> uploadFiles = new ArrayList<>();
for(MultipartFile multipartFile : multipartFiles) {
if(!multipartFile.isEmpty()) {
uploadFiles.add(uploadFile(savePath, multipartFile));
}
}
return uploadFiles;
}
/**
* 업로드된 파일 삭제
* @param path
* @param originalProfile
* @return
*/
public Boolean removeFile(String path, String saveFileName) {
log.debug("========================================");
log.debug("FileUtil.removeFile()");
log.debug("path : {}", path);
log.debug("saveFileName : {}", saveFileName);
File file = new File(path, saveFileName);
System.gc();
boolean result = file.delete();
log.debug("result : {}", result);
log.debug("========================================");
return result;
}
public Boolean removeFile(String path) {
log.debug("========================================");
log.debug("FileUtil.removeFile()");
log.debug("path : {}", path);
File file = new File(path);
System.gc();
boolean result = file.delete();
log.debug("result : {}", result);
log.debug("========================================");
return result;
}
}

View File

@ -0,0 +1,15 @@
package io.company.localhost.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class UploadFile {
private String originalFileName;
private String saveFileName;
private String extension;
private long fileSize;
private String filePath;
}

View File

@ -65,6 +65,30 @@
) )
</insert> </insert>
<!-- 멀티 첨부파일 저장 -->
<insert id="insertAttachments" parameterType="map">
INSERT INTO commonfil (
CMNBRDSEQ,
CMNFLENAM,
CMNFLEORG,
CMNFLEPAT,
CMNFLEEXT,
CMNFLESIZ,
CMNFLEREG,
CMNFLERDT
) VALUES
<foreach collection="list" item="item" index="index" open="(" separator="),(" close=")">
#{LOCBRDSEQ},
#{item.saveFileName},
#{item.originalFileName},
#{item.filePath},
#{item.extension},
#{item.fileSize},
#{CMNFLEREG},
NOW()
</foreach>
</insert>
<!-- 게시물 상세정보 조회 --> <!-- 게시물 상세정보 조회 -->
<select id="selectBoardDetail" resultType="io.company.localhost.common.dto.MapDto"> <select id="selectBoardDetail" resultType="io.company.localhost.common.dto.MapDto">
SELECT SELECT
@ -253,6 +277,29 @@
WHERE LOCCMTSEQ = #{commentId} WHERE LOCCMTSEQ = #{commentId}
LIMIT 1 LIMIT 1
</select> </select>
<!-- 삭제 파일 정보 조회 -->
<select id="selectDelFileInfo" >
SELECT
CMNFLEPAT
FROM
commonfil
WHERE
CMNFLESEQ in
<foreach collection="array" item="item" separator="," open="(" close=")">
#{item}
</foreach>
</select>
<!-- 파일 정보 삭제 -->
<delete id="deleteFileInfo" >
DELETE FROM
commonfil
WHERE
CMNFLESEQ in
<foreach collection="array" item="item" separator="," open="(" close=")">
#{item}
</foreach>
</delete>
</mapper> </mapper>