Skip to content

Commit

Permalink
feat: 영양사님 엑셀 다운로드 기능 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
dradnats1012 committed Oct 5, 2024
1 parent 8ea4621 commit 6ca0bd1
Show file tree
Hide file tree
Showing 6 changed files with 195 additions and 1 deletion.
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ dependencies {
annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"

// Excel
implementation 'org.apache.poi:poi-ooxml:5.2.5'
implementation 'com.opencsv:opencsv:5.9'
}

clean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@

import static in.koreatech.koin.domain.user.model.UserType.COOP;

import java.time.LocalDate;

import org.springframework.core.io.InputStreamResource;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import in.koreatech.koin.domain.coop.dto.CoopLoginRequest;
import in.koreatech.koin.domain.coop.dto.CoopLoginResponse;
Expand Down Expand Up @@ -70,4 +76,20 @@ ResponseEntity<Void> saveDiningImage(
ResponseEntity<CoopLoginResponse> coopLogin(
@RequestBody @Valid CoopLoginRequest request
);

@ApiResponses(
value = {
@ApiResponse(responseCode = "201"),
@ApiResponse(responseCode = "400", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "403", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "404", content = @Content(schema = @Schema(hidden = true))),
}
)
@Operation(summary = "영양사 식단 엑셀 다운로드")
@GetMapping("/dining/excel")
ResponseEntity<InputStreamResource> generateCoopExcel(
@RequestParam("startDate") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
@RequestParam("endDate") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate,
@RequestParam("isCafeteria") Boolean isCafeteria
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,25 @@

import static in.koreatech.koin.domain.user.model.UserType.COOP;

import java.io.ByteArrayInputStream;
import java.net.URI;
import java.time.LocalDate;

import org.springframework.core.io.InputStreamResource;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import in.koreatech.koin.domain.coop.dto.CoopLoginRequest;
import in.koreatech.koin.domain.coop.dto.CoopLoginResponse;
import in.koreatech.koin.domain.coop.dto.DiningImageRequest;
import in.koreatech.koin.domain.coop.dto.ExcelResponseBuilder;
import in.koreatech.koin.domain.coop.dto.SoldOutRequest;
import in.koreatech.koin.domain.coop.service.CoopService;
import in.koreatech.koin.global.auth.Auth;
Expand Down Expand Up @@ -53,4 +60,15 @@ public ResponseEntity<CoopLoginResponse> coopLogin(
return ResponseEntity.created(URI.create("/"))
.body(response);
}

@GetMapping("/dining/excel")
public ResponseEntity<InputStreamResource> generateCoopExcel(
@RequestParam("startDate") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
@RequestParam("endDate") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate,
@RequestParam(name = "isCafeteria", defaultValue = "false") Boolean isCafeteria
) {
ByteArrayInputStream excelFile = coopService.generateCoopExcel(startDate, endDate, isCafeteria);
return ExcelResponseBuilder.buildExcelResponse(excelFile, startDate, endDate);

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package in.koreatech.koin.domain.coop.dto;

import java.io.ByteArrayInputStream;
import java.time.LocalDate;

import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;

public class ExcelResponseBuilder {

public static ResponseEntity<InputStreamResource> buildExcelResponse(
ByteArrayInputStream excelFile, LocalDate startDate, LocalDate endDate
) {
String fileName = startDate.toString() + " ~ " + endDate.toString() + " menu.xlsx";

HttpHeaders headers = new HttpHeaders();
headers.add("Content-Disposition", "attachment; filename=" + fileName);

return ResponseEntity.ok()
.headers(headers)
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(new InputStreamResource(excelFile));
}
}
122 changes: 121 additions & 1 deletion src/main/java/in/koreatech/koin/domain/coop/service/CoopService.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,27 @@
package in.koreatech.koin.domain.coop.service;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.Clock;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
Expand Down Expand Up @@ -44,6 +62,8 @@ public class CoopService {
private final PasswordEncoder passwordEncoder;
private final JwtProvider jwtProvider;

private final int CELL_NUM = 8;

@Transactional
public void changeSoldOut(SoldOutRequest soldOutRequest) {
Dining dining = diningRepository.getById(soldOutRequest.menuId());
Expand All @@ -53,7 +73,8 @@ public void changeSoldOut(SoldOutRequest soldOutRequest) {
dining.setSoldOut(now);
boolean isOpened = coopShopService.getIsOpened(now, CoopShopType.CAFETERIA, dining.getType(), false);
if (isOpened && diningSoldOutCacheRepository.findById(dining.getPlace()).isEmpty()) {
eventPublisher.publishEvent(new DiningSoldOutEvent(dining.getId(), dining.getPlace(), dining.getType()));
eventPublisher.publishEvent(
new DiningSoldOutEvent(dining.getId(), dining.getPlace(), dining.getType()));
}
} else {
dining.cancelSoldOut();
Expand Down Expand Up @@ -90,4 +111,103 @@ public CoopLoginResponse coopLogin(CoopLoginRequest request) {

return CoopLoginResponse.of(accessToken, savedToken.getRefreshToken());
}

public ByteArrayInputStream generateCoopExcel(LocalDate startDate, LocalDate endDate, Boolean isCafeteria) {
List<Dining> dinings;

if(isCafeteria) {
List<String> placeFilters = Arrays.asList("A코너", "B코너", "C코너");
dinings = diningRepository.findByDateBetweenAndPlaceIn(startDate, endDate, placeFilters);
} else {
dinings = diningRepository.findByDateBetween(startDate, endDate);
}


try (Workbook workbook = new XSSFWorkbook()) {
Sheet sheet = workbook.createSheet("식단 메뉴");

CellStyle headerStyle = makeHeaderStyle(workbook);
CellStyle commonStyle = makeCommonStyle(workbook);
createHeaderCell(sheet, headerStyle);

return putDiningData(dinings, sheet, commonStyle, workbook);

} catch (IOException e) {
throw new RuntimeException("엑셀 파일 생성 중 오류가 발생했습니다.", e);
}
}

private ByteArrayInputStream putDiningData(List<Dining> dinings, Sheet sheet, CellStyle commonStyle,
Workbook workbook) throws IOException {
AtomicInteger rowIdx = new AtomicInteger(1);

dinings.forEach(dining -> {
Row row = sheet.createRow(rowIdx.getAndIncrement());
row.createCell(0).setCellValue(dining.getDate().toString());
row.createCell(1).setCellValue(dining.getType().toString());
row.createCell(2).setCellValue(dining.getPlace());
row.createCell(3).setCellValue(dining.getKcal() != null ? dining.getKcal() : 0);

String formattedMenu = dining.getMenu().toString()
.replaceAll("^\\[|\\]$", "")
.replaceAll(", ", "\n");

Cell menuCell = row.createCell(4);
menuCell.setCellValue(formattedMenu);

row.createCell(5).setCellValue(dining.getImageUrl());
row.createCell(6).setCellValue(dining.getSoldOut());
row.createCell(7).setCellValue(dining.getIsChanged());

for (int i = 0; i < 8; i++) {
row.getCell(i).setCellStyle(commonStyle);
}
});

for (int i = 0; i < CELL_NUM; i++) {
sheet.autoSizeColumn(i);
sheet.setColumnWidth(i, (sheet.getColumnWidth(i) + 1024));
}

ByteArrayOutputStream out = new ByteArrayOutputStream();
workbook.write(out);
return new ByteArrayInputStream(out.toByteArray());
}

private void createHeaderCell(Sheet sheet, CellStyle headerStyle) {
Row headerRow = sheet.createRow(0);
headerRow.createCell(0).setCellValue("날짜");
headerRow.createCell(1).setCellValue("타입");
headerRow.createCell(2).setCellValue("코너");
headerRow.createCell(3).setCellValue("칼로리");
headerRow.createCell(4).setCellValue("메뉴");
headerRow.createCell(5).setCellValue("이미지");
headerRow.createCell(6).setCellValue("품절 여부");
headerRow.createCell(7).setCellValue("변경 여부");

for (int i = 0; i < CELL_NUM; i++) {
Cell cell = headerRow.getCell(i);
cell.setCellStyle(headerStyle);
}
}

private static CellStyle makeCommonStyle(Workbook workbook) {
CellStyle commonStyle = workbook.createCellStyle();
commonStyle.setAlignment(HorizontalAlignment.CENTER);
commonStyle.setVerticalAlignment(VerticalAlignment.CENTER);
commonStyle.setWrapText(true);
return commonStyle;
}

private static CellStyle makeHeaderStyle(Workbook workbook) {
CellStyle headerStyle = workbook.createCellStyle();
Font font = workbook.createFont();
font.setBold(true);
font.setColor(IndexedColors.WHITE.getIndex());
headerStyle.setFont(font);
headerStyle.setFillForegroundColor(IndexedColors.LIGHT_BLUE.getIndex());
headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
headerStyle.setAlignment(HorizontalAlignment.CENTER);
return headerStyle;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,8 @@ default Dining getById(Integer id) {
Long count();

Page<Dining> findAllByMenuContainingAndPlaceIn(String keyword, List<String> diningPlaces, Pageable pageable);

List<Dining> findByDateBetween(LocalDate startDate, LocalDate endDate);

List<Dining> findByDateBetweenAndPlaceIn(LocalDate startDate, LocalDate endDate, List<String> placeFilters);
}

0 comments on commit 6ca0bd1

Please sign in to comment.