diff --git a/build.gradle b/build.gradle index dce5710..a246b01 100644 --- a/build.gradle +++ b/build.gradle @@ -46,6 +46,8 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' // flying saucer implementation 'org.xhtmlrenderer:flying-saucer-pdf:9.9.1' + // markdown + implementation 'org.commonmark:commonmark:0.21.0' // postgre implementation 'org.postgresql:postgresql:42.7.4' runtimeOnly 'org.postgresql:postgresql' diff --git a/src/main/java/com/swOnCampus/AIPlatform/AiPlatformApplication.java b/src/main/java/com/swOnCampus/AIPlatform/AiPlatformApplication.java index 724c298..c5cb701 100644 --- a/src/main/java/com/swOnCampus/AIPlatform/AiPlatformApplication.java +++ b/src/main/java/com/swOnCampus/AIPlatform/AiPlatformApplication.java @@ -15,8 +15,8 @@ }) public class AiPlatformApplication { - public static void main(String[] args) { - SpringApplication.run(AiPlatformApplication.class, args); - } + public static void main(String[] args) { + SpringApplication.run(AiPlatformApplication.class, args); + } } diff --git a/src/main/java/com/swOnCampus/AIPlatform/domain/report/service/ReportService.java b/src/main/java/com/swOnCampus/AIPlatform/domain/report/service/ReportService.java index e5f0402..e568b89 100644 --- a/src/main/java/com/swOnCampus/AIPlatform/domain/report/service/ReportService.java +++ b/src/main/java/com/swOnCampus/AIPlatform/domain/report/service/ReportService.java @@ -1,14 +1,8 @@ package com.swOnCampus.AIPlatform.domain.report.service; -import com.swOnCampus.AIPlatform.domain.report.web.dto.ReportingSummaryRequest; +import com.swOnCampus.AIPlatform.domain.report.web.dto.ReportingResponse; public interface ReportService { - /** - * 리포팅 요약된 내용을 html로 변환 하여 pdf를 생성하는 메서드 - * - * @param request : 리포팅 요약 데이터를 담은 request 객체 - * @return : 생성된 PDF 파일의 바이트 배열 - */ - byte[] createReportingSummaryPdf(ReportingSummaryRequest request); -} + ReportingResponse createReportingPdf(Long memberId, Long companyId); +} \ No newline at end of file diff --git a/src/main/java/com/swOnCampus/AIPlatform/domain/report/service/ReportServiceImpl.java b/src/main/java/com/swOnCampus/AIPlatform/domain/report/service/ReportServiceImpl.java index 7d09469..dc9c2f0 100644 --- a/src/main/java/com/swOnCampus/AIPlatform/domain/report/service/ReportServiceImpl.java +++ b/src/main/java/com/swOnCampus/AIPlatform/domain/report/service/ReportServiceImpl.java @@ -1,13 +1,18 @@ package com.swOnCampus.AIPlatform.domain.report.service; import com.lowagie.text.pdf.BaseFont; -import com.swOnCampus.AIPlatform.domain.report.web.dto.ReportingSummaryRequest; +import com.swOnCampus.AIPlatform.domain.consulting.entity.Consulting; +import com.swOnCampus.AIPlatform.domain.consulting.exception.ConsultingErrorCode; +import com.swOnCampus.AIPlatform.domain.consulting.repository.ConsultingRepository; +import com.swOnCampus.AIPlatform.domain.report.exception.ReportErrorCode; +import com.swOnCampus.AIPlatform.domain.report.web.dto.ReportingResponse; import com.swOnCampus.AIPlatform.global.exception.GlobalException; import java.io.ByteArrayOutputStream; +import java.util.Base64; import java.util.Map; - -import com.swOnCampus.AIPlatform.domain.report.exception.ReportErrorCode; import lombok.RequiredArgsConstructor; +import org.commonmark.parser.Parser; +import org.commonmark.renderer.html.HtmlRenderer; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.ClassPathResource; import org.springframework.stereotype.Service; @@ -20,19 +25,31 @@ public class ReportServiceImpl implements ReportService { private final TemplateEngine templateEngine; + private final ConsultingRepository consultingRepository; @Value(value = "${report.font.path}") private String fontPath; @Override - public byte[] createReportingSummaryPdf(ReportingSummaryRequest request) { + public ReportingResponse createReportingPdf(Long memberId, Long companyId) { + Consulting consulting = consultingRepository.findByCompanyId(companyId) + .orElseThrow(() -> new GlobalException(ConsultingErrorCode.NOT_EXIST_CONSULTING)); + + String summaryHtml = consulting.getSummary().replace("\n", "
"); + String renderedMarkdown = renderMarkdown(summaryHtml); + Map contextVariables = Map.of( - "title", request.title(), - "content", request.content() + "content", renderedMarkdown ); String html = createHtmlFromTemplate(contextVariables); + byte[] pdfBytes = convertHtmlToPdf(html); - return convertHtmlToPdf(html); + ReportingResponse response = new ReportingResponse( + consulting.getSummary(), + Base64.getEncoder().encodeToString(pdfBytes) + ); + + return response; } private String createHtmlFromTemplate(Map contextVariables) { @@ -57,12 +74,18 @@ private byte[] convertHtmlToPdf(String html) { private void setFont(ITextRenderer renderer) { try { renderer.getFontResolver().addFont( - new ClassPathResource(fontPath).getURL().toString(), - BaseFont.IDENTITY_H, - BaseFont.EMBEDDED + new ClassPathResource(fontPath).getURL().toString(), + BaseFont.IDENTITY_H, + BaseFont.EMBEDDED ); } catch (Exception e) { throw new GlobalException(ReportErrorCode.NOT_FOUND_FONT); } } -} + + private String renderMarkdown(String markdownText) { + Parser parser = Parser.builder().build(); + HtmlRenderer renderer = HtmlRenderer.builder().build(); + return renderer.render(parser.parse(markdownText)); + } +} \ No newline at end of file diff --git a/src/main/java/com/swOnCampus/AIPlatform/domain/report/web/controller/ReportController.java b/src/main/java/com/swOnCampus/AIPlatform/domain/report/web/controller/ReportController.java index 905eb37..dfadebd 100644 --- a/src/main/java/com/swOnCampus/AIPlatform/domain/report/web/controller/ReportController.java +++ b/src/main/java/com/swOnCampus/AIPlatform/domain/report/web/controller/ReportController.java @@ -1,15 +1,24 @@ package com.swOnCampus.AIPlatform.domain.report.web.controller; +import com.swOnCampus.AIPlatform.domain.member.entity.Member; import com.swOnCampus.AIPlatform.domain.report.service.ReportService; -import com.swOnCampus.AIPlatform.domain.report.web.dto.ReportingSummaryRequest; +import com.swOnCampus.AIPlatform.domain.report.web.dto.ReportingResponse; +import com.swOnCampus.AIPlatform.global.annotation.LoginMember; +import com.swOnCampus.AIPlatform.global.response.ApiResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; -import org.springframework.http.MediaType; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +@Tag(name = "컨설팅 PDF 생성 API", description = "컨설팅 결과 PDF 생성 관련 API") @RequiredArgsConstructor @RestController @RequestMapping("/api/v1/report") @@ -17,20 +26,34 @@ public class ReportController { private final ReportService reportService; + @Operation(summary = "컨설팅 PDF API 요청", description = "컨설팅 결과 PDF 생성 API 요청") + @ApiResponses(value = { + @io.swagger.v3.oas.annotations.responses.ApiResponse( + responseCode = "COMMON200", + description = "요청 성공", + content = { + @Content( + schema = @Schema( + implementation = ReportingResponse.class + ) + ) + } + ) + }) @GetMapping() - public ResponseEntity createReportingSummaryPdf(Model model) { - model.addAttribute("title", "리포팅 요약"); - model.addAttribute("content", "내용"); + public ResponseEntity> createReportingPdf( + @LoginMember Member member, + @RequestParam Long companyId // 채팅방 id + ) { + ReportingResponse reportingResponse = reportService.createReportingPdf( + member.getMemberId(), companyId); - ReportingSummaryRequest request = ReportingSummaryRequest.builder() - .title(model.getAttribute("title").toString()) - .content(model.getAttribute("content").toString()) - .build(); + ApiResponse response = ApiResponse.createSuccess( + HttpStatus.OK.value(), + reportingResponse, + "PDF 파일이 생성되었습니다." + ); - byte[] pdfData = reportService.createReportingSummaryPdf(request); - - return ResponseEntity.ok() - .contentType(MediaType.APPLICATION_PDF) - .body(pdfData); + return ResponseEntity.ok(response); } -} +} \ No newline at end of file diff --git a/src/main/java/com/swOnCampus/AIPlatform/domain/report/web/dto/ReportingResponse.java b/src/main/java/com/swOnCampus/AIPlatform/domain/report/web/dto/ReportingResponse.java new file mode 100644 index 0000000..33c9b46 --- /dev/null +++ b/src/main/java/com/swOnCampus/AIPlatform/domain/report/web/dto/ReportingResponse.java @@ -0,0 +1,14 @@ +package com.swOnCampus.AIPlatform.domain.report.web.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; + +@Builder +public record ReportingResponse( + @Schema(description = "컨설팅 요약 내용", example = "컨설팅 요약") + String summary, + @Schema(description = "컨설팅 전체 내용 PDF 파일의 Base64 인코딩 데이터", example = "byte") + String pdf +) { + +} \ No newline at end of file diff --git a/src/main/java/com/swOnCampus/AIPlatform/domain/report/web/dto/ReportingSummaryRequest.java b/src/main/java/com/swOnCampus/AIPlatform/domain/report/web/dto/ReportingSummaryRequest.java deleted file mode 100644 index e941d8f..0000000 --- a/src/main/java/com/swOnCampus/AIPlatform/domain/report/web/dto/ReportingSummaryRequest.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.swOnCampus.AIPlatform.domain.report.web.dto; - -import lombok.Builder; - -@Builder -public record ReportingSummaryRequest( - String title, - String content -) { - -} \ No newline at end of file diff --git a/src/main/resources/template/reporting-summary-template.html b/src/main/resources/templates/reporting-summary-template.html similarity index 85% rename from src/main/resources/template/reporting-summary-template.html rename to src/main/resources/templates/reporting-summary-template.html index 6019212..daa2306 100644 --- a/src/main/resources/template/reporting-summary-template.html +++ b/src/main/resources/templates/reporting-summary-template.html @@ -16,7 +16,7 @@ -

-

+

컨설팅 결과

+

\ No newline at end of file