55import ceos .ipx .domain .analysis .inventivestep .entity .InventiveStepAnalysis ;
66import ceos .ipx .domain .analysis .inventivestep .repository .InventiveArgumentRepository ;
77import ceos .ipx .domain .analysis .inventivestep .repository .InventiveStepAnalysisRepository ;
8+ import ceos .ipx .domain .analysis .novelty .entity .ComparisonResult ;
89import ceos .ipx .domain .analysis .novelty .entity .NoveltyAnalysis ;
910import ceos .ipx .domain .analysis .novelty .entity .NoveltyComparison ;
1011import ceos .ipx .domain .analysis .novelty .repository .NoveltyAnalysisRepository ;
1415import ceos .ipx .domain .cases .entity .PriorArt ;
1516import ceos .ipx .domain .cases .repository .CaseRepository ;
1617import ceos .ipx .domain .cases .repository .InventionComponentRepository ;
17- import ceos .ipx .domain .report .dto .request .ReportCreateRequest ;
1818import ceos .ipx .domain .report .dto .request .ReportUpdateRequest ;
1919import ceos .ipx .domain .report .dto .response .ReportCreateResponse ;
2020import ceos .ipx .domain .report .dto .response .ReportDetailResponse ;
@@ -46,32 +46,76 @@ public class ReportService {
4646 private final InventiveArgumentRepository inventiveArgumentRepository ;
4747 private final ReportRepository reportRepository ;
4848
49+ /**
50+ * 저장된 신규성·진보성 분석 결과를 바탕으로
51+ * 분석 리포트를 자동 생성한다.
52+ */
4953 @ Transactional
50- public ReportCreateResponse saveReport (
54+ public ReportCreateResponse createReport (
5155 Long userId ,
52- Long caseId ,
53- ReportCreateRequest request
56+ Long caseId
5457 ) {
5558 Case caseEntity = caseRepository .findById (caseId )
5659 .orElseThrow (() ->
5760 new BusinessException (ErrorCode .CASE_NOT_FOUND )
5861 );
5962
6063 validateCaseOwner (caseEntity , userId );
61- validateAnalysisResults (caseEntity );
6264
6365 if (reportRepository .findByCaseEntity (caseEntity ).isPresent ()) {
6466 throw new BusinessException (
6567 ErrorCode .REPORT_ALREADY_EXISTS
6668 );
6769 }
6870
71+ NoveltyAnalysis noveltyAnalysis =
72+ noveltyAnalysisRepository .findByCaseEntity (caseEntity )
73+ .orElseThrow (() ->
74+ new BusinessException (
75+ ErrorCode .NOVELTY_ANALYSIS_NOT_FOUND
76+ )
77+ );
78+
79+ InventiveStepAnalysis inventiveStepAnalysis =
80+ inventiveStepAnalysisRepository
81+ .findByCaseEntity (caseEntity )
82+ .orElseThrow (() ->
83+ new BusinessException (
84+ ErrorCode .INVENTIVE_STEP_ANALYSIS_NOT_FOUND
85+ )
86+ );
87+
88+ List <NoveltyComparison > comparisons =
89+ noveltyComparisonRepository
90+ .findAllByAnalysis (noveltyAnalysis );
91+
92+ List <InventiveArgument > arguments =
93+ inventiveArgumentRepository
94+ .findAllByAnalysis (inventiveStepAnalysis );
95+
96+ boolean noveltySatisfied =
97+ isNoveltySatisfied (comparisons );
98+
99+ List <InventiveArgument > recommendedArguments =
100+ getRecommendedArguments (arguments );
101+
102+ boolean inventiveSatisfied =
103+ !recommendedArguments .isEmpty ();
104+
105+ String overallConclusion =
106+ buildOverallConclusion (
107+ noveltyAnalysis ,
108+ recommendedArguments ,
109+ noveltySatisfied ,
110+ inventiveSatisfied
111+ );
112+
69113 Report report = Report .builder ()
70114 .caseEntity (caseEntity )
71- .authorName (request . authorName ().trim ())
72- .noveltySatisfied (request . noveltySatisfied () )
73- .inventiveSatisfied (request . inventiveSatisfied () )
74- .overallConclusion (request . overallConclusion (). trim () )
115+ .authorName (caseEntity . getUser ().getName ())
116+ .noveltySatisfied (noveltySatisfied )
117+ .inventiveSatisfied (inventiveSatisfied )
118+ .overallConclusion (overallConclusion )
75119 .build ();
76120
77121 reportRepository .save (report );
@@ -81,7 +125,10 @@ public ReportCreateResponse saveReport(
81125 reportRepository .flush ();
82126 caseRepository .flush ();
83127
84- return ReportCreateResponse .of (report , caseEntity );
128+ return ReportCreateResponse .of (
129+ report ,
130+ caseEntity
131+ );
85132 }
86133
87134 @ Transactional
@@ -111,7 +158,10 @@ public ReportUpdateResponse updateReport(
111158
112159 reportRepository .flush ();
113160
114- return ReportUpdateResponse .of (report , caseEntity );
161+ return ReportUpdateResponse .of (
162+ report ,
163+ caseEntity
164+ );
115165 }
116166
117167 public ReportDetailResponse getReport (
@@ -198,6 +248,88 @@ public ReportDetailResponse getReport(
198248 .build ();
199249 }
200250
251+ /**
252+ * 신규성 비교 결과 중 NOVEL 판정이 하나라도 있으면
253+ * 신규성을 충족한 것으로 판단한다.
254+ */
255+ private boolean isNoveltySatisfied (
256+ List <NoveltyComparison > comparisons
257+ ) {
258+ return comparisons .stream ()
259+ .anyMatch (comparison ->
260+ comparison .getComparisonResult ()
261+ == ComparisonResult .NOVEL
262+ );
263+ }
264+
265+ /**
266+ * 진보성 논리 중 AI가 추천한 항목만 반환한다.
267+ */
268+ private List <InventiveArgument > getRecommendedArguments (
269+ List <InventiveArgument > arguments
270+ ) {
271+ return arguments .stream ()
272+ .filter (argument ->
273+ Boolean .TRUE .equals (
274+ argument .getRecommended ()
275+ )
276+ )
277+ .toList ();
278+ }
279+
280+ /**
281+ * 신규성 분석 결론과 추천된 진보성 논리를 조합해
282+ * 리포트의 종합 결론을 자동 생성한다.
283+ */
284+ private String buildOverallConclusion (
285+ NoveltyAnalysis noveltyAnalysis ,
286+ List <InventiveArgument > recommendedArguments ,
287+ boolean noveltySatisfied ,
288+ boolean inventiveSatisfied
289+ ) {
290+ String noveltyConclusion =
291+ noveltyAnalysis .getConclusionText ().trim ();
292+
293+ String inventiveArgumentLabels =
294+ recommendedArguments .stream ()
295+ .map (argument ->
296+ argument .getArgumentType ().getLabel ()
297+ )
298+ .collect (Collectors .joining (", " ));
299+
300+ if (noveltySatisfied && inventiveSatisfied ) {
301+ return String .format (
302+ "%s 또한 %s 논리가 도출되어, "
303+ + "본 발명은 신규성과 진보성을 모두 충족할 가능성이 "
304+ + "있는 것으로 판단됩니다." ,
305+ noveltyConclusion ,
306+ inventiveArgumentLabels
307+ );
308+ }
309+
310+ if (noveltySatisfied ) {
311+ return String .format (
312+ "%s 다만 적용 가능한 진보성 논리가 확인되지 않아, "
313+ + "진보성 충족 여부는 추가 검토가 필요합니다." ,
314+ noveltyConclusion
315+ );
316+ }
317+
318+ if (inventiveSatisfied ) {
319+ return String .format (
320+ "%s %s 논리를 통한 진보성 주장은 가능하나, "
321+ + "신규성 부정 가능성에 대한 추가 검토가 필요합니다." ,
322+ noveltyConclusion ,
323+ inventiveArgumentLabels
324+ );
325+ }
326+
327+ return String .format (
328+ "%s 신규성과 진보성 모두 추가 검토가 필요한 것으로 판단됩니다." ,
329+ noveltyConclusion
330+ );
331+ }
332+
201333 private ReportDetailResponse .ComponentResponse
202334 toComponentResponse (
203335 InventionComponent component
@@ -390,24 +522,4 @@ private void validateCaseOwner(
390522 );
391523 }
392524 }
393-
394- private void validateAnalysisResults (
395- Case caseEntity
396- ) {
397- noveltyAnalysisRepository .findByCaseEntity (caseEntity )
398- .orElseThrow (() ->
399- new BusinessException (
400- ErrorCode .NOVELTY_ANALYSIS_NOT_FOUND
401- )
402- );
403-
404- inventiveStepAnalysisRepository
405- .findByCaseEntity (caseEntity )
406- .orElseThrow (() ->
407- new BusinessException (
408- ErrorCode
409- .INVENTIVE_STEP_ANALYSIS_NOT_FOUND
410- )
411- );
412- }
413525}
0 commit comments