diff --git a/src/main/java/com/xit/biz/cmm/controller/CmmFileController.java b/src/main/java/com/xit/biz/cmm/controller/CmmFileController.java new file mode 100644 index 0000000..8960e99 --- /dev/null +++ b/src/main/java/com/xit/biz/cmm/controller/CmmFileController.java @@ -0,0 +1,96 @@ +package com.xit.biz.cmm.controller; + +import com.xit.biz.ctgy.dto.MinInfoBoard680Dto; +import com.xit.biz.ctgy.entity.MinInfoBoard680; +import com.xit.biz.ctgy.service.ICtgyFileService; +import com.xit.core.api.IRestResponse; +import com.xit.core.api.RestResponse; +import com.xit.core.constant.ErrorCode; +import com.xit.core.exception.CustomBaseException; +import com.xit.core.util.AssertUtils; +import com.xit.core.util.Checks; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.apache.commons.io.FileUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.env.Environment; +import org.springframework.core.io.InputStreamResource; +import org.springframework.core.io.Resource; +import org.springframework.http.*; +import org.springframework.lang.NonNull; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Nonnull; +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.IOException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; + +@Tag(name = "CtgyFileMgtController", description = "공지사항 / 게시판 관리") +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/v1/ctgy/cmm") +public class CmmFileController { + + private final Environment env; + + @Value("${file.cmm.upload.root:c:/data/file/upload}") + private String rootPath; + + @Value("${file.cmm.upload.path:/kangnamSIM/simUpFile/}") + private String uploadPath; + + @Value("${file.cmm.upload.url}") + private String serviceUrl; + + private final ICtgyFileService service; + + @GetMapping("/download/{inCode}") + public void download(@PathVariable Long inCode, HttpServletResponse response) { + + MinInfoBoard680 entity = service.findFiles(inCode); + + String absFile = ""; + + if (Arrays.asList(env.getActiveProfiles()).contains("prod")) + absFile = entity.getInFileurl() + File.separator + entity.getInFilename(); + else + absFile = rootPath + entity.getInFileurl().split(serviceUrl)[1] + File.separator + entity.getInFilename(); + + Path path = Paths.get(absFile); + String contentType = null; + try { + contentType = Files.probeContentType(path); + } catch (IOException e) { + throw new CustomBaseException(ErrorCode.FILE_NOT_FOUND); + } + + File file = new File(absFile); + byte[] fileByte = new byte[0]; + try { + fileByte = FileUtils.readFileToByteArray(file); + } catch (IOException e) { + throw new CustomBaseException(ErrorCode.FILE_NOT_FOUND); + } + + response.setContentType(contentType); + response.setHeader(HttpHeaders.CONTENT_TYPE, contentType); + response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true"); + response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + URLEncoder.encode(entity.getInFilename(), StandardCharsets.UTF_8) + "\";"); + //response.setHeader(HttpHeaders.CONTENT_ENCODING, "binary"); + response.setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(entity.getInFilesize())); + try { + response.getOutputStream().write(fileByte); + response.getOutputStream().flush(); + response.getOutputStream().close(); + } catch (IOException e) { + throw new CustomBaseException(ErrorCode.FILE_NOT_FOUND); + } + } +} diff --git a/src/main/java/com/xit/biz/cmm/service/ICmmFileService.java b/src/main/java/com/xit/biz/cmm/service/ICmmFileService.java index da917f4..d2b024d 100644 --- a/src/main/java/com/xit/biz/cmm/service/ICmmFileService.java +++ b/src/main/java/com/xit/biz/cmm/service/ICmmFileService.java @@ -1,19 +1,12 @@ package com.xit.biz.cmm.service; -import com.xit.biz.ctgy.entity.MinInfoBoard680; import org.springframework.web.multipart.MultipartFile; -import javax.annotation.Nonnull; -import java.util.List; - /** * @author Lim, Jong Uk (minuk926) * @since 2021-07-16 */ public interface ICmmFileService { - MinInfoBoard680 findFiles(Long inCode); - - List saveFiles(@Nonnull MinInfoBoard680 minInfoBoard680, MultipartFile[] files); + String uploadFiles(MultipartFile[] files, String rootPath, String uploadPath); - void removePublicBoardFile(Long inCode); } diff --git a/src/main/java/com/xit/biz/cmm/service/impl/CmmFileService.java b/src/main/java/com/xit/biz/cmm/service/impl/CmmFileService.java index ba672c1..eb7a7f4 100644 --- a/src/main/java/com/xit/biz/cmm/service/impl/CmmFileService.java +++ b/src/main/java/com/xit/biz/cmm/service/impl/CmmFileService.java @@ -1,16 +1,8 @@ package com.xit.biz.cmm.service.impl; import com.xit.biz.cmm.service.ICmmFileService; -import com.xit.biz.ctgy.CtgyConstants; -import com.xit.biz.ctgy.entity.MinInfoBoard680; -import com.xit.biz.ctgy.repository.IPublicBoardRepository; -import com.xit.core.constant.ErrorCode; -import com.xit.core.exception.CustomBaseException; -import com.xit.core.support.jpa.JpaUtil; import com.xit.core.util.AssertUtils; -import com.xit.core.util.Checks; import com.xit.core.util.DateUtil; -import io.jsonwebtoken.lang.Assert; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -19,11 +11,8 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import org.springframework.web.multipart.MultipartFile; -import javax.annotation.Nonnull; import java.io.File; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; import java.util.Objects; @Slf4j @@ -31,12 +20,6 @@ import java.util.Objects; @RequiredArgsConstructor public class CmmFileService implements ICmmFileService { - @Value("${file.cmm.upload.root:c:/data/file/upload}") - private String rootPath; - - @Value("${file.cmm.upload.path:/kangnamSIM/simUpFile/}") - private String uploadPath; - @Value("${file.cmm.upload.url}") private String serviceUrl; @@ -46,36 +29,25 @@ public class CmmFileService implements ICmmFileService { @Value("${file.cmm.upload.max.size:1024}") private long maxSize; - private final IPublicBoardRepository repository; - - @Override - public MinInfoBoard680 findFiles(Long inCode) { - Assert.notNull(inCode, "대상 게시글[inCode]을 선택해 주세요."); - - return repository.findById(inCode).orElse(null); - } - /** - * 파일 등록 - * 신규등록시는 CmmFileMst.fileMstId가 null, 변경시 not null - * 변경시 동일한 파일 이름이 존재하면 DB 및 해당 파일 삭제후 신규로 생성 - * - * @param entity MinInfoBoard680 + * 파일 업로드 + * 업로드된 최종 경로 return(ex : 20220406) + * -> rootPath + uploadPath + 해당경로 + fileName * @param files MultipartFile[] - * @return CmmFileMst + * @return String makePath */ @Override @Transactional - public List saveFiles(@Nonnull MinInfoBoard680 entity, MultipartFile[] files) { - List entityList = new ArrayList<>(); + public String uploadFiles(MultipartFile[] files, String rootPath, String uploadPath) { + String makePath = ""; if(files != null && files.length > 0){ - String makePath = File.separator + DateUtil.getToday(""); + makePath = File.separator + DateUtil.getToday(""); // 파일 경로 : upload root 제외 - String urlPath = this.uploadPath + makePath; + String urlPath = uploadPath + makePath; // 물리적인 파일 저장 위치 - String fileUploadPath = this.rootPath + urlPath; + String fileUploadPath = rootPath + urlPath; File file = new File(fileUploadPath); if(!file.exists()) file.mkdirs(); @@ -84,20 +56,6 @@ public class CmmFileService implements ICmmFileService { String orgFileName = ""; try { orgFileName = StringUtils.cleanPath(Objects.requireNonNull(mf.getOriginalFilename())); - MinInfoBoard680 savedEntity = null; - - // 파일 저장 && 전송 - if(Checks.isEmpty(entity.getInCode())) - savedEntity = new MinInfoBoard680(); - else - savedEntity = repository.findById(entity.getInCode()).orElseGet(MinInfoBoard680::new); - savedEntity.setInFilename(orgFileName); - savedEntity.setInFilesize(mf.getSize()); - savedEntity.setInFileurl(serviceUrl + urlPath); - setEntity(savedEntity, entity); - JpaUtil.saveIfNullId(entity.getInCode(), repository, savedEntity); - - entityList.add(savedEntity); mf.transferTo(new File(fileUploadPath + File.separator + orgFileName)); // inputStream을 가져와 @@ -113,49 +71,8 @@ public class CmmFileService implements ICmmFileService { } } } - }else{ - MinInfoBoard680 savedEntity = null; - if(Checks.isEmpty(entity.getInCode())) - savedEntity = new MinInfoBoard680(); - else - savedEntity = repository.findById(entity.getInCode()).orElseGet(MinInfoBoard680::new); - - setEntity(savedEntity, entity); - JpaUtil.saveIfNullId(entity.getInCode(), repository, savedEntity); - } - return entityList; - } - - @Override - @Transactional - public void removePublicBoardFile(Long inCode) { - - MinInfoBoard680 savedEntity = repository.findById(inCode).orElseThrow(() -> new CustomBaseException(ErrorCode.NOT_FOUND)); - repository.delete(savedEntity); - - // 정보 삭제후 파일 삭제 : 에러 발생시 skip - if(Checks.isNotEmpty(savedEntity.getInFilename())){ - String absFile = rootPath + savedEntity.getInFileurl().split(serviceUrl)[1]+File.separator + savedEntity.getInFilename(); - try { - File file = new File(absFile); - if (Checks.isNotEmpty(file) && file.exists()) file.delete(); - }catch(Exception e){ - // - } - } - - } - - private void setEntity(MinInfoBoard680 savedEntity, MinInfoBoard680 entity){ - savedEntity.setInBgubun(CtgyConstants.PublicBoard.GUBUN.getCode()); - savedEntity.setInDept(entity.getInDept()); - savedEntity.setInTitle(entity.getInTitle()); - savedEntity.setInContents(entity.getInContents()); - - if(Checks.isEmpty(entity.getInCode())) { - savedEntity.setInCode(repository.getInCodeByInBgubun()); - savedEntity.setInContentno(savedEntity.getInCode()); } + return makePath; } } diff --git a/src/main/java/com/xit/biz/ctgy/controller/ResidentController.java b/src/main/java/com/xit/biz/ctgy/controller/ResidentController.java index 0b73fd8..5231361 100644 --- a/src/main/java/com/xit/biz/ctgy/controller/ResidentController.java +++ b/src/main/java/com/xit/biz/ctgy/controller/ResidentController.java @@ -52,7 +52,8 @@ public class ResidentController { public ResponseEntity saveResidentData(@Nonnull GnRecallScDto dto) { AssertUtils.isTrue(!Checks.isEmpty(dto), "등록할 거주자 의견진술 자료가 존재하지 않습니다."); - service.saveResidentData(mapstruct.toEntity(dto), dto.getPicadFiles(), dto.getFrecadFiles(), dto.getContadFiles()); + service.saveResidentData(dto); + //service.saveResidentData(mapstruct.toEntity(dto), dto.getPicadFiles(), dto.getFrecadFiles(), dto.getContadFiles()); return RestResponse.of(HttpStatus.OK); // return RestResponse.of(HttpStatus.OK); } diff --git a/src/main/java/com/xit/biz/ctgy/entity/GnRecallSc.java b/src/main/java/com/xit/biz/ctgy/entity/GnRecallSc.java index 4af79f9..14e83dc 100644 --- a/src/main/java/com/xit/biz/ctgy/entity/GnRecallSc.java +++ b/src/main/java/com/xit/biz/ctgy/entity/GnRecallSc.java @@ -12,13 +12,19 @@ import java.util.Objects; @Schema(name = "GnRecallSc", description = "") @Table(name = "gn_recall_sc", schema = "traffic", catalog = "") @Entity +@SequenceGenerator( + name = "RECALL_SC_SEQ_GEN", + sequenceName = "GN_RECALL_SC_SEQ", + allocationSize = 1 // default = 50 이므로 반드시 setting 필요 +) @Getter @NoArgsConstructor @AllArgsConstructor @Builder public class GnRecallSc { - @Schema(required = true, title = " ", example = " ", description = " ") @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "RECALL_SC_SEQ_GEN") + @Schema(required = true, title = " ", example = " ", description = " ") @Column(name = "sc_code") private Long scCode; @Schema(required = true, title = " ", example = " ", description = " ") @@ -124,6 +130,16 @@ public class GnRecallSc { @Column(name = "sc_bunji") private String scBunji; + @PrePersist + public void onPrePersist(){ + this.scDatagb = "1"; + } + + @PreUpdate + public void onPreUpdate(){ + } + + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/main/java/com/xit/biz/ctgy/entity/MinSimsa680.java b/src/main/java/com/xit/biz/ctgy/entity/MinSimsa680.java index 5b6dd0c..a8476da 100644 --- a/src/main/java/com/xit/biz/ctgy/entity/MinSimsa680.java +++ b/src/main/java/com/xit/biz/ctgy/entity/MinSimsa680.java @@ -1,13 +1,9 @@ package com.xit.biz.ctgy.entity; -import com.xit.biz.cmm.entity.CmmUser; import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; import javax.persistence.*; -import java.io.Serializable; -import java.util.HashSet; -import java.util.Set; @Schema(name = "MinSimsa680", description = "민원심사") @Table(name = "min_simsa680", schema = "", catalog = "") diff --git a/src/main/java/com/xit/biz/ctgy/entity/MinSimsaUser680.java b/src/main/java/com/xit/biz/ctgy/entity/MinSimsaUser680.java index f7b2ef0..d0f3f56 100644 --- a/src/main/java/com/xit/biz/ctgy/entity/MinSimsaUser680.java +++ b/src/main/java/com/xit/biz/ctgy/entity/MinSimsaUser680.java @@ -1,13 +1,10 @@ package com.xit.biz.ctgy.entity; -import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; import javax.persistence.*; import java.io.Serializable; -import java.util.HashSet; -import java.util.Set; @Schema(name = "MinSimsaUser680", description = "민원심사사용자매핑") @Table(name = "min_simsa_user680", schema = "", catalog = "") diff --git a/src/main/java/com/xit/biz/ctgy/repository/IResidentRepository.java b/src/main/java/com/xit/biz/ctgy/repository/IResidentRepository.java index a365601..e92d56c 100644 --- a/src/main/java/com/xit/biz/ctgy/repository/IResidentRepository.java +++ b/src/main/java/com/xit/biz/ctgy/repository/IResidentRepository.java @@ -2,7 +2,15 @@ package com.xit.biz.ctgy.repository; import com.xit.biz.ctgy.entity.GnRecallSc; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; public interface IResidentRepository extends JpaRepository, IResidentRepositoryCustom { + + // TODO : Ansi - sql 미사용 + //@Query(value = "SELECT max(e.sc_code) + 1 FROM gn_recall_sc e, nativeQuery = true) + @Query(value = "SELECT rpad(nvl(max(e.sc_seq), :year), 10, '0') + 1 FROM gn_recall_sc e WHERE e.sc_datagb = '1' AND e.sc_seq LIKE :year||'%' ", nativeQuery = true) + Long getGnRecallScMaxScSeq(@Param("year") String year); + GnRecallSc findByScCode(final Long scCode); } diff --git a/src/main/java/com/xit/biz/ctgy/service/IResidentService.java b/src/main/java/com/xit/biz/ctgy/service/IResidentService.java index d56d777..56c1837 100644 --- a/src/main/java/com/xit/biz/ctgy/service/IResidentService.java +++ b/src/main/java/com/xit/biz/ctgy/service/IResidentService.java @@ -17,5 +17,5 @@ public interface IResidentService { Page findAll(final GnRecallSc entity, Pageable pageable); - void saveResidentData(GnRecallSc entity, MultipartFile[] picadFiles, MultipartFile[] frecadFiles, MultipartFile[] contadFiles); + void saveResidentData(GnRecallScDto entity); } diff --git a/src/main/java/com/xit/biz/ctgy/service/impl/ResidentService.java b/src/main/java/com/xit/biz/ctgy/service/impl/ResidentService.java index d69fc85..3b59e2d 100644 --- a/src/main/java/com/xit/biz/ctgy/service/impl/ResidentService.java +++ b/src/main/java/com/xit/biz/ctgy/service/impl/ResidentService.java @@ -1,23 +1,44 @@ package com.xit.biz.ctgy.service.impl; +import com.xit.biz.cmm.service.ICmmFileService; import com.xit.biz.ctgy.dto.GnRecallScDto; +import com.xit.biz.ctgy.dto.struct.GnRecallScMapstruct; import com.xit.biz.ctgy.entity.*; import com.xit.biz.ctgy.repository.*; import com.xit.biz.ctgy.service.IResidentService; import com.xit.core.support.jpa.JpaUtil; +import com.xit.core.util.DateUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.mapstruct.factory.Mappers; +import org.springframework.beans.factory.annotation.Value; import org.springframework.data.domain.*; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; import org.springframework.web.multipart.MultipartFile; +import java.io.File; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Objects; + @Service @RequiredArgsConstructor @Slf4j public class ResidentService implements IResidentService { + @Value("${file.cmm.upload.root:c:/data/file/upload}") + private String rootPath; + + @Value("${file.cmm.upload.simsaPath:[simUpFile_sc1]}") + private String[] uploadPath; + private final IResidentRepository repository; + private final GnRecallScMapstruct mapstruct = Mappers.getMapper(GnRecallScMapstruct.class); + + private final ICmmFileService fileService; @Override @Transactional(readOnly = true) @@ -54,7 +75,39 @@ public class ResidentService implements IResidentService { } @Override - public void saveResidentData(GnRecallSc entity, MultipartFile[] picadFiles, MultipartFile[] frecadFiles, MultipartFile[] contadFiles) { - log.debug("{}{}{}{}", entity, picadFiles, frecadFiles, contadFiles); + public void saveResidentData(GnRecallScDto dto) { + + if(dto.getPicadFiles() != null) { + setFileInfoAndFileUpload(dto, dto.getPicadFiles(), "setScPicad"); + } + + if(dto.getFrecadFiles() != null) { + setFileInfoAndFileUpload(dto, dto.getFrecadFiles(), "setScPicad"); + } + + if(dto.getContadFiles() != null) { + setFileInfoAndFileUpload(dto, dto.getContadFiles(), "setScContad"); + } + + // 접수번호 채번 : 년도 + seq 10자리 + dto.setScSeq(repository.getGnRecallScMaxScSeq(String.valueOf(DateUtil.getCurrentYear()))); + GnRecallSc entity = mapstruct.toEntity(dto); + repository.save(entity); + + } + + private void setFileInfoAndFileUpload(GnRecallScDto dto, MultipartFile[] mfs, String setMethodName) { + String makePath = fileService.uploadFiles(mfs, rootPath, uploadPath[0]); + makePath = makePath + File.separator; + + for(int idx = 0; idx < mfs.length; idx++){ + MultipartFile mf = mfs[idx]; + try { + Method method = GnRecallScDto.class.getMethod(setMethodName + (idx+1), String.class); + method.invoke(dto, StringUtils.cleanPath(makePath + mf.getOriginalFilename())); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + } } } diff --git a/src/main/resources/config/conf.yml b/src/main/resources/config/conf.yml index 1f6f728..538d278 100644 --- a/src/main/resources/config/conf.yml +++ b/src/main/resources/config/conf.yml @@ -21,6 +21,8 @@ file: # root: /Users/minuk/data/file/upload # 공지사항 path: /kangnamSIM/simUpFile + publicPath: simUpFile #/kangnamSIM/simUpFile + simsaPath: simUpFile_sc1, simUpFile_sc2 # 거주자 simUpFile_sc1, 장애인 simUpFile_sc2 url: http://traffic.gangnam.go.kr allow: ext: