File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
package kr.itn.itnhub.review;
import kr.itn.itnhub.org.OrgNotFoundException;
import kr.itn.itnhub.org.Organization;
import kr.itn.itnhub.org.OrganizationMapper;
import kr.itn.itnhub.seed.SeedParseException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
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.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
/** 기관관리 상세의 권리확인 탭이 부르는 엔드포인트. */
@RestController
public class ReviewController {
private final ReviewService reviewService;
private final ReviewImportService importService;
private final OrganizationMapper orgMapper;
public ReviewController(ReviewService reviewService, ReviewImportService importService,
OrganizationMapper orgMapper) {
this.reviewService = reviewService;
this.importService = importService;
this.orgMapper = orgMapper;
}
@PostMapping("/api/orgs/{id}/review/import")
public ReviewImportReport upload(@PathVariable("id") Long orgId, @RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
throw new SeedParseException("업로드된 파일이 비어 있습니다.");
}
String filename = file.getOriginalFilename();
if (filename == null || !hasAllowedExtension(filename)) {
throw new SeedParseException("엑셀 파일(.xlsx 또는 .xlsm)만 업로드할 수 있습니다.");
}
try (InputStream in = file.getInputStream()) {
return importService.importFile(orgId, in);
} catch (IOException e) {
throw new SeedParseException("엑셀 파일을 읽지 못했습니다.", e);
}
}
/**
* keyword는 사이트명/게시판명/제목 통합검색, site/board/title은 열별 조건(서로 AND)이다.
*/
@GetMapping("/api/orgs/{id}/review")
public ReviewPage list(@PathVariable("id") Long orgId,
@RequestParam(required = false) String keyword,
@RequestParam(required = false) String site,
@RequestParam(required = false) String board,
@RequestParam(required = false) String title,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "30") int size) {
return reviewService.list(orgId, keyword, site, board, title, page, size);
}
/** 목록에서 체크한 건들에 같은 값을 한 번에 반영한다. 반영된 건수를 돌려준다. */
@PutMapping("/api/orgs/{id}/review/bulk")
public java.util.Map<String, Integer> updateBulk(@PathVariable("id") Long orgId,
@RequestBody BulkJudgmentRequest request) {
return java.util.Map.of("updated", reviewService.updateBulk(orgId, request));
}
/** 목록에서 체크한 건들을 지운다. */
@PostMapping("/api/orgs/{id}/review/bulk-delete")
public java.util.Map<String, Integer> deleteBulk(@PathVariable("id") Long orgId,
@RequestBody java.util.List<Long> ids) {
return java.util.Map.of("deleted", reviewService.deleteBulk(orgId, ids));
}
@GetMapping("/api/orgs/{id}/review/{itemId}")
public ReviewItem get(@PathVariable("id") Long orgId, @PathVariable Long itemId) {
return reviewService.get(orgId, itemId);
}
@PutMapping("/api/orgs/{id}/review/{itemId}")
public ReviewItem update(@PathVariable("id") Long orgId, @PathVariable Long itemId,
@RequestBody JudgmentRequest request) {
return reviewService.updateJudgment(orgId, itemId, request);
}
@ResponseStatus(HttpStatus.NO_CONTENT)
@DeleteMapping("/api/orgs/{id}/review/{itemId}")
public void delete(@PathVariable("id") Long orgId, @PathVariable Long itemId) {
reviewService.delete(orgId, itemId);
}
/** 한글 파일명이라 ChannelFeedController.download와 동일하게 RFC 5987 인코딩을 쓴다. */
@GetMapping("/api/orgs/{id}/review/download")
public ResponseEntity<byte[]> download(@PathVariable("id") Long orgId) {
Organization org = orgMapper.findById(orgId);
if (org == null) {
throw new OrgNotFoundException("기관을 찾을 수 없습니다: " + orgId);
}
byte[] content = reviewService.downloadWorkbook(orgId);
String filename = "권리확인_" + org.getOrgName() + ".xlsx";
String encodedName = URLEncoder.encode(filename, StandardCharsets.UTF_8).replace("+", "%20");
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename*=UTF-8''" + encodedName)
.contentType(MediaType.parseMediaType(
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"))
.body(content);
}
private static boolean hasAllowedExtension(String filename) {
String lower = filename.toLowerCase();
return lower.endsWith(".xlsx") || lower.endsWith(".xlsm");
}
}