diff --git a/settings.gradle b/settings.gradle index d3c84324..318cbef3 100644 --- a/settings.gradle +++ b/settings.gradle @@ -7,6 +7,7 @@ include 'smeem-input-http' include 'smeem-application' include 'smeem-output-persistence' include 'smeem-output-persistence:postgresql' +include 'smeem-output-persistence:mongodb' include 'smeem-output-notification' include 'smeem-output-notification:firebase' include 'smeem-output-oauth' diff --git a/smeem-application/src/main/java/com/smeem/application/domain/auth/AuthService.java b/smeem-application/src/main/java/com/smeem/application/domain/auth/AuthService.java index f854bfac..006bca2c 100644 --- a/smeem-application/src/main/java/com/smeem/application/domain/auth/AuthService.java +++ b/smeem-application/src/main/java/com/smeem/application/domain/auth/AuthService.java @@ -7,6 +7,7 @@ import com.smeem.application.port.input.dto.response.auth.GenerateTokenResponse; import com.smeem.application.port.input.dto.response.auth.SignInResponse; import com.smeem.application.port.output.oauth.OauthPort; +import com.smeem.application.port.output.persistence.CorrectionPort; import com.smeem.application.port.output.persistence.MemberPort; import com.smeem.common.logger.HookLogger; import com.smeem.common.logger.LoggingMessage; @@ -19,6 +20,7 @@ @RequiredArgsConstructor @Transactional(readOnly = true) public class AuthService implements AuthUseCase { + private final CorrectionPort correctionPort; private final MemberPort memberPort; private final OauthPort oauthPort; private final TokenGenerator tokenGenerator; @@ -54,6 +56,7 @@ public void signOut(long memberId) { @Transactional public void withdraw(long memberId, WithdrawRequest request) { + correctionPort.deleteByMember(memberId); memberPort.deleteById(memberId); createWithdrawal(request); } diff --git a/smeem-application/src/main/java/com/smeem/application/domain/diary/Correction.java b/smeem-application/src/main/java/com/smeem/application/domain/diary/Correction.java index fc1c9e21..2baba242 100644 --- a/smeem-application/src/main/java/com/smeem/application/domain/diary/Correction.java +++ b/smeem-application/src/main/java/com/smeem/application/domain/diary/Correction.java @@ -14,6 +14,9 @@ public record Correction( String correctedSentence, @Schema(description = "교정 사유", example = "스펠링 틀림") @JsonProperty("reason") - String reason + String reason, + @Schema(description = "교정 여부", example = "true") + @JsonProperty("is_corrected") + boolean isCorrected ) { } diff --git a/smeem-application/src/main/java/com/smeem/application/domain/diary/CorrectionService.java b/smeem-application/src/main/java/com/smeem/application/domain/diary/CorrectionService.java index 6d18d248..1a0c60a3 100644 --- a/smeem-application/src/main/java/com/smeem/application/domain/diary/CorrectionService.java +++ b/smeem-application/src/main/java/com/smeem/application/domain/diary/CorrectionService.java @@ -30,12 +30,15 @@ public CorrectionsResponse correctDiary(long memberId, long diaryId) { LocalDate today = LocalDate.now(); String key = getCorrectionCacheKey(memberId, today); + // 제한 횟수 검증 int correctionCount = getOrUpdateCorrectionCount(key, memberId, today); smeemProperties.getLimit().validateCorrectionLimit(correctionCount); + // 일기 소유권 검증 Diary diary = diaryPort.findById(diaryId); diary.validateDiaryOwnership(memberId); + // AI 첨삭 및 캐시 업데이트 List corrections = createCorrections(diary); cachePort.incrementInt(key); @@ -60,6 +63,6 @@ private int updateCacheWithCorrectionCount(String key, long memberId, LocalDate private List createCorrections(Diary diary) { List corrections = openAiPort.promptCorrections(diary.getContent()); - return correctionPort.saveAll(corrections, diary); + return correctionPort.save(corrections, diary); } } diff --git a/smeem-application/src/main/java/com/smeem/application/port/output/persistence/CorrectionPort.java b/smeem-application/src/main/java/com/smeem/application/port/output/persistence/CorrectionPort.java index 0b2716cc..737a8178 100644 --- a/smeem-application/src/main/java/com/smeem/application/port/output/persistence/CorrectionPort.java +++ b/smeem-application/src/main/java/com/smeem/application/port/output/persistence/CorrectionPort.java @@ -7,11 +7,13 @@ import java.util.List; public interface CorrectionPort { - List saveAll(List corrections, Diary diary); + List save(List corrections, Diary diary); int countDistinctByMemberAndDate(long memberId, LocalDate date); List findByDiary(long diary); void deleteByDiary(long diaryId); + + void deleteByMember(long memberId); } diff --git a/smeem-bootstrap/build.gradle b/smeem-bootstrap/build.gradle index bfd66c7b..09cb101d 100644 --- a/smeem-bootstrap/build.gradle +++ b/smeem-bootstrap/build.gradle @@ -4,6 +4,7 @@ dependencies { implementation project(':smeem-input-http') implementation project(':smeem-output-persistence') implementation project(':smeem-output-persistence:postgresql') + implementation project(':smeem-output-persistence:mongodb') implementation project(':smeem-output-notification') implementation project(':smeem-output-notification:firebase') implementation project(':smeem-output-oauth') diff --git a/smeem-bootstrap/src/main/resources/application-dev.yml b/smeem-bootstrap/src/main/resources/application-dev.yml index 84548e8e..06b783d4 100644 --- a/smeem-bootstrap/src/main/resources/application-dev.yml +++ b/smeem-bootstrap/src/main/resources/application-dev.yml @@ -4,6 +4,7 @@ spring: on-profile: dev import: - classpath:postgres-config/application-dev.yml + - classpath:mongo-config/application-dev.yml - classpath:smeem-config/application-dev.yml - classpath:notification-config/application-dev.yml - classpath:common-config/application-dev.yml diff --git a/smeem-bootstrap/src/main/resources/application-local.yml b/smeem-bootstrap/src/main/resources/application-local.yml index c4efcc01..85fc1df1 100644 --- a/smeem-bootstrap/src/main/resources/application-local.yml +++ b/smeem-bootstrap/src/main/resources/application-local.yml @@ -4,6 +4,7 @@ spring: on-profile: local import: - classpath:postgres-config/application-local.yml + - classpath:mongo-config/application-local.yml - classpath:smeem-config/application-local.yml - classpath:notification-config/application-local.yml - classpath:common-config/application-local.yml diff --git a/smeem-bootstrap/src/main/resources/application-prod.yml b/smeem-bootstrap/src/main/resources/application-prod.yml index bf3eb50b..49c558b8 100644 --- a/smeem-bootstrap/src/main/resources/application-prod.yml +++ b/smeem-bootstrap/src/main/resources/application-prod.yml @@ -4,6 +4,7 @@ spring: on-profile: prod import: - classpath:postgres-config/application-prod.yml + - classpath:mongo-config/application-prod.yml - classpath:smeem-config/application-prod.yml - classpath:notification-config/application-prod.yml - classpath:common-config/application-prod.yml diff --git a/smeem-output-persistence/build.gradle b/smeem-output-persistence/build.gradle index 61245d57..c0b90e2e 100644 --- a/smeem-output-persistence/build.gradle +++ b/smeem-output-persistence/build.gradle @@ -1,6 +1,8 @@ project(':smeem-output-persistence:postgresql') { dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + + // postgresql implementation 'org.postgresql:postgresql' // QueryDSL @@ -11,6 +13,13 @@ project(':smeem-output-persistence:postgresql') { } } +project(':smeem-output-persistence:mongodb') { + dependencies { + // mongodb + implementation 'org.springframework.boot:spring-boot-starter-data-mongodb' + } +} + allprojects { dependencies { implementation project(':smeem-common') diff --git a/smeem-output-persistence/mongodb/src/main/java/com/smeem/output/persistence/mongodb/adapter/CorrectionAdapter.java b/smeem-output-persistence/mongodb/src/main/java/com/smeem/output/persistence/mongodb/adapter/CorrectionAdapter.java new file mode 100644 index 00000000..1a8c76a2 --- /dev/null +++ b/smeem-output-persistence/mongodb/src/main/java/com/smeem/output/persistence/mongodb/adapter/CorrectionAdapter.java @@ -0,0 +1,50 @@ +package com.smeem.output.persistence.mongodb.adapter; + +import com.smeem.application.domain.diary.Correction; +import com.smeem.application.domain.diary.Diary; +import com.smeem.application.port.output.persistence.CorrectionPort; +import com.smeem.output.persistence.mongodb.persistence.document.CorrectionDocument; +import com.smeem.output.persistence.mongodb.persistence.repository.CorrectionRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +@Component +@RequiredArgsConstructor +public class CorrectionAdapter implements CorrectionPort { + private final CorrectionRepository correctionRepository; + + @Override + public List save(List corrections, Diary diary) { + CorrectionDocument savedCorrection = correctionRepository.save(new CorrectionDocument(corrections, diary)); + return savedCorrection.toDomain(); + } + + @Override + public int countDistinctByMemberAndDate(long memberId, LocalDate date) { + LocalDateTime startOfDay = date.atStartOfDay(); + LocalDateTime endOfDay = date.plusDays(1).atStartOfDay(); + return correctionRepository.countByMemberIdAndCreatedAtBetween(memberId, startOfDay, endOfDay); + } + + @Override + public List findByDiary(long diaryId) { + return correctionRepository.findByDiaryId(diaryId) + .map(CorrectionDocument::toDomain) + .orElseGet(ArrayList::new); + } + + @Override + public void deleteByDiary(long diaryId) { + correctionRepository.deleteByDiaryId(diaryId); + } + + @Override + public void deleteByMember(long memberId) { + correctionRepository.deleteByMemberId(memberId); + } +} diff --git a/smeem-output-persistence/mongodb/src/main/java/com/smeem/output/persistence/mongodb/config/MongoConfig.java b/smeem-output-persistence/mongodb/src/main/java/com/smeem/output/persistence/mongodb/config/MongoConfig.java new file mode 100644 index 00000000..2ed417a5 --- /dev/null +++ b/smeem-output-persistence/mongodb/src/main/java/com/smeem/output/persistence/mongodb/config/MongoConfig.java @@ -0,0 +1,29 @@ +package com.smeem.output.persistence.mongodb.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.mongodb.MongoDatabaseFactory; +import org.springframework.data.mongodb.config.EnableMongoAuditing; +import org.springframework.data.mongodb.core.convert.DbRefResolver; +import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver; +import org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper; +import org.springframework.data.mongodb.core.convert.MappingMongoConverter; +import org.springframework.data.mongodb.core.mapping.MongoMappingContext; +import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; + +@Configuration +@EnableMongoAuditing +@EnableMongoRepositories(basePackages = "com.smeem.output.persistence.mongodb") +public class MongoConfig { + + @Bean + public MappingMongoConverter mappingMongoConverter( + MongoDatabaseFactory mongoDatabaseFactory, + MongoMappingContext mongoMappingContext + ) { + DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDatabaseFactory); + MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mongoMappingContext); + converter.setTypeMapper(new DefaultMongoTypeMapper(null)); + return converter; + } +} diff --git a/smeem-output-persistence/mongodb/src/main/java/com/smeem/output/persistence/mongodb/persistence/document/CorrectionDocument.java b/smeem-output-persistence/mongodb/src/main/java/com/smeem/output/persistence/mongodb/persistence/document/CorrectionDocument.java new file mode 100644 index 00000000..aaebc735 --- /dev/null +++ b/smeem-output-persistence/mongodb/src/main/java/com/smeem/output/persistence/mongodb/persistence/document/CorrectionDocument.java @@ -0,0 +1,61 @@ +package com.smeem.output.persistence.mongodb.persistence.document; + +import com.smeem.application.domain.diary.Correction; +import com.smeem.application.domain.diary.Diary; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; + +import java.time.LocalDateTime; +import java.util.List; + +@Document(collection = "smeem") +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class CorrectionDocument { + @Id + private String id; + private List corrections; + private long memberId; + private long diaryId; + @CreatedDate + private LocalDateTime createdAt; + + @NoArgsConstructor(access = AccessLevel.PROTECTED) + @Getter + private static class CorrectionDetail { + private String originContent; + private String correctedContent; + private String reason; + private boolean corrected; + + public CorrectionDetail(Correction correction) { + this.originContent = correction.originalSentence(); + this.correctedContent = correction.correctedSentence(); + this.corrected = correction.isCorrected(); + this.reason = corrected ? correction.reason() : null; + } + + public Correction toDomain() { + return Correction.builder() + .originalSentence(originContent) + .correctedSentence(correctedContent) + .reason(reason) + .isCorrected(corrected) + .build(); + } + } + + public CorrectionDocument(List corrections, Diary diary) { + this.corrections = corrections.stream().map(CorrectionDetail::new).toList(); + this.memberId = diary.getMemberId(); + this.diaryId = diary.getId(); + } + + public List toDomain() { + return this.corrections.stream().map(CorrectionDetail::toDomain).toList(); + } +} diff --git a/smeem-output-persistence/mongodb/src/main/java/com/smeem/output/persistence/mongodb/persistence/repository/CorrectionRepository.java b/smeem-output-persistence/mongodb/src/main/java/com/smeem/output/persistence/mongodb/persistence/repository/CorrectionRepository.java new file mode 100644 index 00000000..488e9bf7 --- /dev/null +++ b/smeem-output-persistence/mongodb/src/main/java/com/smeem/output/persistence/mongodb/persistence/repository/CorrectionRepository.java @@ -0,0 +1,19 @@ +package com.smeem.output.persistence.mongodb.persistence.repository; + +import com.smeem.output.persistence.mongodb.persistence.document.CorrectionDocument; +import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.stereotype.Repository; + +import java.time.LocalDateTime; +import java.util.Optional; + +@Repository +public interface CorrectionRepository extends MongoRepository { + int countByMemberIdAndCreatedAtBetween(long memberId, LocalDateTime startAt, LocalDateTime endAt); + + Optional findByDiaryId(long diaryId); + + void deleteByDiaryId(long diaryId); + + void deleteByMemberId(long memberId); +} diff --git a/smeem-output-persistence/mongodb/src/main/resources/mongo-config/application-dev.yml b/smeem-output-persistence/mongodb/src/main/resources/mongo-config/application-dev.yml new file mode 100644 index 00000000..92508d89 --- /dev/null +++ b/smeem-output-persistence/mongodb/src/main/resources/mongo-config/application-dev.yml @@ -0,0 +1,8 @@ +spring: + config: + activate: + on-profile: dev + + data: + mongodb: + uri: mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${MONGO_HOST}:27017/smeem?authSource=admin diff --git a/smeem-output-persistence/mongodb/src/main/resources/mongo-config/application-local.yml b/smeem-output-persistence/mongodb/src/main/resources/mongo-config/application-local.yml new file mode 100644 index 00000000..f25539d2 --- /dev/null +++ b/smeem-output-persistence/mongodb/src/main/resources/mongo-config/application-local.yml @@ -0,0 +1,8 @@ +spring: + config: + activate: + on-profile: local + + data: + mongodb: + uri: mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${MONGO_HOST}:27017/smeem?authSource=admin diff --git a/smeem-output-persistence/mongodb/src/main/resources/mongo-config/application-prod.yml b/smeem-output-persistence/mongodb/src/main/resources/mongo-config/application-prod.yml new file mode 100644 index 00000000..0864abba --- /dev/null +++ b/smeem-output-persistence/mongodb/src/main/resources/mongo-config/application-prod.yml @@ -0,0 +1,8 @@ +spring: + config: + activate: + on-profile: prod + + data: + mongodb: + uri: mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${MONGO_HOST}:27017/smeem?authSource=admin diff --git a/smeem-output-persistence/postgresql/src/main/java/com/smeem/persistence/postgresql/adapter/CorrectionAdapter.java b/smeem-output-persistence/postgresql/src/main/java/com/smeem/persistence/postgresql/adapter/CorrectionAdapter.java deleted file mode 100644 index 808fd27a..00000000 --- a/smeem-output-persistence/postgresql/src/main/java/com/smeem/persistence/postgresql/adapter/CorrectionAdapter.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.smeem.persistence.postgresql.adapter; - -import com.smeem.application.domain.diary.Correction; -import com.smeem.application.domain.diary.Diary; -import com.smeem.application.port.output.persistence.CorrectionPort; -import com.smeem.persistence.postgresql.persistence.entity.CorrectionEntity; -import com.smeem.persistence.postgresql.persistence.repository.diary.CorrectionRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Component; - -import java.time.LocalDate; -import java.util.List; -import java.util.UUID; - -@Component -@RequiredArgsConstructor -public class CorrectionAdapter implements CorrectionPort { - private final CorrectionRepository correctionRepository; - - @Override - public List saveAll(List corrections, Diary diary) { - UUID key = UUID.randomUUID(); - return correctionRepository.saveAll( - corrections.stream().map(it -> new CorrectionEntity(it, diary, key)).toList() - ).stream().map(CorrectionEntity::toDomain).toList(); - } - - @Override - public int countDistinctByMemberAndDate(long memberId, LocalDate date) { - return correctionRepository.countDistinctKeyByMemberIdAndCreatedAt(memberId, date); - } - - @Override - public List findByDiary(long diaryId) { - return correctionRepository.findByDiaryId(diaryId).stream().map(CorrectionEntity::toDomain).toList(); - } - - @Override - public void deleteByDiary(long diaryId) { - correctionRepository.deleteByDiaryId(diaryId); - } -} diff --git a/smeem-output-persistence/postgresql/src/main/java/com/smeem/persistence/postgresql/adapter/MemberAdapter.java b/smeem-output-persistence/postgresql/src/main/java/com/smeem/persistence/postgresql/adapter/MemberAdapter.java index 7974539f..70a46b46 100644 --- a/smeem-output-persistence/postgresql/src/main/java/com/smeem/persistence/postgresql/adapter/MemberAdapter.java +++ b/smeem-output-persistence/postgresql/src/main/java/com/smeem/persistence/postgresql/adapter/MemberAdapter.java @@ -8,7 +8,6 @@ import com.smeem.persistence.postgresql.persistence.entity.MemberEntity; import com.smeem.persistence.postgresql.persistence.entity.WithdrawEntity; import com.smeem.persistence.postgresql.persistence.repository.*; -import com.smeem.persistence.postgresql.persistence.repository.diary.CorrectionRepository; import lombok.RequiredArgsConstructor; import lombok.val; import org.springframework.stereotype.Repository; @@ -20,7 +19,6 @@ @Repository @RequiredArgsConstructor public class MemberAdapter implements MemberPort { - private final CorrectionRepository correctionRepository; private final MemberRepository memberRepository; private final MemberBadgeRepository memberBadgeRepository; private final DeletedDiaryRepository deletedDiaryRepository; @@ -57,7 +55,6 @@ public void deleteById(long id) { diaryRepository.deleteByMemberId(id); trainingTimeRepository.deleteByMemberId(id); memberRepository.deleteById(id); - correctionRepository.deleteByMemberId(id); } @Override diff --git a/smeem-output-persistence/postgresql/src/main/java/com/smeem/persistence/postgresql/persistence/entity/CorrectionEntity.java b/smeem-output-persistence/postgresql/src/main/java/com/smeem/persistence/postgresql/persistence/entity/CorrectionEntity.java deleted file mode 100644 index f213acd2..00000000 --- a/smeem-output-persistence/postgresql/src/main/java/com/smeem/persistence/postgresql/persistence/entity/CorrectionEntity.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.smeem.persistence.postgresql.persistence.entity; - -import com.smeem.application.domain.diary.Correction; -import com.smeem.application.domain.diary.Diary; -import jakarta.persistence.*; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; - -import java.util.UUID; - -@Entity -@Table(name = "correction", schema = "smeem") -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class CorrectionEntity extends BaseEntity { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @Column(columnDefinition = "TEXT", nullable = false) - private String originContent; - - @Column(columnDefinition = "TEXT", nullable = false) - private String correctedContent; - - @Column(columnDefinition = "TEXT", nullable = false) - private String reason; - - @Column(nullable = false) - private long memberId; - - @Column(nullable = false) - private long diaryId; - - @Column(nullable = false) - private UUID key; - - public CorrectionEntity(Correction correction, Diary diary, UUID key) { - this.originContent = correction.originalSentence(); - this.correctedContent = correction.correctedSentence(); - this.reason = correction.reason(); - this.memberId = diary.getMemberId(); - this.diaryId = diary.getId(); - this.key = key; - } - - public Correction toDomain() { - return Correction.builder() - .originalSentence(originContent) - .correctedSentence(correctedContent) - .reason(reason) - .build(); - } -} diff --git a/smeem-output-persistence/postgresql/src/main/java/com/smeem/persistence/postgresql/persistence/repository/diary/CorrectionRepository.java b/smeem-output-persistence/postgresql/src/main/java/com/smeem/persistence/postgresql/persistence/repository/diary/CorrectionRepository.java deleted file mode 100644 index 11a22a92..00000000 --- a/smeem-output-persistence/postgresql/src/main/java/com/smeem/persistence/postgresql/persistence/repository/diary/CorrectionRepository.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.smeem.persistence.postgresql.persistence.repository.diary; - -import com.smeem.persistence.postgresql.persistence.entity.CorrectionEntity; -import com.smeem.persistence.postgresql.persistence.repository.diary.custom.CorrectionCustomRepository; -import org.springframework.data.jpa.repository.JpaRepository; - -import java.util.List; - -public interface CorrectionRepository extends JpaRepository, CorrectionCustomRepository { - void deleteByDiaryId(long diaryId); - - void deleteByMemberId(long memberId); - - List findByDiaryId(long diaryId); -} diff --git a/smeem-output-persistence/postgresql/src/main/java/com/smeem/persistence/postgresql/persistence/repository/diary/custom/CorrectionCustomRepository.java b/smeem-output-persistence/postgresql/src/main/java/com/smeem/persistence/postgresql/persistence/repository/diary/custom/CorrectionCustomRepository.java deleted file mode 100644 index 02dc21a8..00000000 --- a/smeem-output-persistence/postgresql/src/main/java/com/smeem/persistence/postgresql/persistence/repository/diary/custom/CorrectionCustomRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.smeem.persistence.postgresql.persistence.repository.diary.custom; - -import java.time.LocalDate; - -public interface CorrectionCustomRepository { - int countDistinctKeyByMemberIdAndCreatedAt(long memberId, LocalDate date); -} diff --git a/smeem-output-persistence/postgresql/src/main/java/com/smeem/persistence/postgresql/persistence/repository/diary/custom/CorrectionRepositoryImpl.java b/smeem-output-persistence/postgresql/src/main/java/com/smeem/persistence/postgresql/persistence/repository/diary/custom/CorrectionRepositoryImpl.java deleted file mode 100644 index 6cfc21e1..00000000 --- a/smeem-output-persistence/postgresql/src/main/java/com/smeem/persistence/postgresql/persistence/repository/diary/custom/CorrectionRepositoryImpl.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.smeem.persistence.postgresql.persistence.repository.diary.custom; - -import com.querydsl.jpa.impl.JPAQueryFactory; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; - -import static com.smeem.persistence.postgresql.persistence.entity.QCorrectionEntity.correctionEntity; - -@Repository -@RequiredArgsConstructor -public class CorrectionRepositoryImpl implements CorrectionCustomRepository { - private final JPAQueryFactory queryFactory; - - @Override - public int countDistinctKeyByMemberIdAndCreatedAt(long memberId, LocalDate date) { - LocalDateTime startOfDay = date.atStartOfDay(); - LocalDateTime endOfDay = date.atTime(LocalTime.MAX); - - return (int) queryFactory - .select(correctionEntity.key) - .from(correctionEntity) - .where( - correctionEntity.createdAt.between(startOfDay, endOfDay), - correctionEntity.memberId.eq(memberId) - ) - .distinct() - .stream().count(); - } -} diff --git a/smeem-output-web/gpt/src/main/java/com/smeem/output/web/gpt/adapter/OpenAiAdapter.java b/smeem-output-web/gpt/src/main/java/com/smeem/output/web/gpt/adapter/OpenAiAdapter.java index aa5becfa..b7d51fd7 100644 --- a/smeem-output-web/gpt/src/main/java/com/smeem/output/web/gpt/adapter/OpenAiAdapter.java +++ b/smeem-output-web/gpt/src/main/java/com/smeem/output/web/gpt/adapter/OpenAiAdapter.java @@ -7,6 +7,8 @@ import com.smeem.common.exception.ExceptionCode; import com.smeem.common.exception.SmeemException; import com.smeem.output.web.gpt.adapter.dto.CorrectionsResponse; +import com.smeem.output.web.gpt.adapter.template.PromptTemplate; +import com.smeem.output.web.gpt.adapter.template.SchemeTemplate; import lombok.RequiredArgsConstructor; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.model.ChatResponse; @@ -18,6 +20,8 @@ import java.util.List; +import static org.springframework.ai.openai.api.OpenAiApi.ChatCompletionRequest.ResponseFormat.Type.JSON_SCHEMA; + @Component @RequiredArgsConstructor public class OpenAiAdapter implements OpenAiPort { @@ -39,34 +43,11 @@ public String prompt(String message) { @Override public List promptCorrections(String content) { - String jsonSchema = """ - { - "type": "object", - "properties": { - "results": { - "type": "array", - "items": { - "type": "object", - "properties": { - "original_sentence": {"type": "string"}, - "corrected_sentence": {"type": "string"}, - "reason": {"type": "string"} - }, - "required": ["original_sentence", "corrected_sentence", "reason"], - "additionalProperties": false - } - } - }, - "required": ["results"], - "additionalProperties": false - } - """; - OpenAiChatOptions options = OpenAiChatOptions.builder() - .withResponseFormat(new ResponseFormat(ResponseFormat.Type.JSON_SCHEMA, jsonSchema)) + .withResponseFormat(new ResponseFormat(JSON_SCHEMA, SchemeTemplate.getCorrectionScheme())) .build(); - Prompt prompt = new Prompt(getCorrectionPrompt(content), options); + Prompt prompt = new Prompt(PromptTemplate.getCorrectionPrompt(content), options); ChatResponse call = chatModel.call(prompt); String response = call.getResult().getOutput().getContent(); @@ -76,15 +57,4 @@ public List promptCorrections(String content) { throw new SmeemException(ExceptionCode.OPEN_AI_SERVICE_AVAILABLE, e.getMessage()); } } - - private String getCorrectionPrompt(String content) { - return String.format(""" - Paragraph : %s - - JSON response description: - - original_sentence: Separate sentence from paragraph, including whitespace, spaces and special characters exactly as they appear in the original. - - corrected_sentence: corrected sentence including correct expressions. - - reason: this value MUST be Korean. reason is why the sentence is corrected. - """, content); - } } diff --git a/smeem-output-web/gpt/src/main/java/com/smeem/output/web/gpt/adapter/template/PromptTemplate.java b/smeem-output-web/gpt/src/main/java/com/smeem/output/web/gpt/adapter/template/PromptTemplate.java new file mode 100644 index 00000000..aaf2246a --- /dev/null +++ b/smeem-output-web/gpt/src/main/java/com/smeem/output/web/gpt/adapter/template/PromptTemplate.java @@ -0,0 +1,19 @@ +package com.smeem.output.web.gpt.adapter.template; + +public class PromptTemplate { + public static String getCorrectionPrompt(String content) { + return String.format(""" + Paragraph : %s + + Please correct the following English sentences, determine whether each sentence is correct or incorrect, and generate a JSON response in the specified format. + Make sure to keep the order of the sentences. + The JSON format should be as follows: + + JSON response description: + - original_sentence: The original sentence exactly as it appears. + - corrected_sentence: The corrected sentence (if correct, repeat the original). + - reason: The reason for the correction, explained in Korean. + - is_correct: Whether the sentence has been corrected. Set to true if corrected, and false if unchanged. + """, content); + } +} diff --git a/smeem-output-web/gpt/src/main/java/com/smeem/output/web/gpt/adapter/template/SchemeTemplate.java b/smeem-output-web/gpt/src/main/java/com/smeem/output/web/gpt/adapter/template/SchemeTemplate.java new file mode 100644 index 00000000..a83c6b4b --- /dev/null +++ b/smeem-output-web/gpt/src/main/java/com/smeem/output/web/gpt/adapter/template/SchemeTemplate.java @@ -0,0 +1,29 @@ +package com.smeem.output.web.gpt.adapter.template; + +public class SchemeTemplate { + public static String getCorrectionScheme() { + return """ + { + "type": "object", + "properties": { + "results": { + "type": "array", + "items": { + "type": "object", + "properties": { + "original_sentence": {"type": "string"}, + "corrected_sentence": {"type": "string"}, + "reason": {"type": "string"}, + "is_corrected": {"type": "boolean"} + }, + "required": ["original_sentence", "corrected_sentence", "reason", "is_corrected"], + "additionalProperties": false + } + } + }, + "required": ["results"], + "additionalProperties": false + } + """; + } +}