Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.swyp.catsgotogedog.aiplanner.controller;

import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
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.RestController;

import com.swyp.catsgotogedog.aiplanner.request.PlannerRequest;
import com.swyp.catsgotogedog.aiplanner.service.AiPlannerService;
import com.swyp.catsgotogedog.global.CatsgotogedogApiResponse;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/planner")
@Slf4j
public class AiplannerController implements AiplannerControllerSwagger {

private final AiPlannerService aiPlannerService;

@PostMapping("/initEmbeddingData")
public void initEmbeddingData() {
aiPlannerService.initEmbedContentData();
}

@PostMapping("/recommend")
public ResponseEntity<CatsgotogedogApiResponse<?>> createAiPlanner(
@AuthenticationPrincipal String userId,
@RequestBody PlannerRequest request
) {
return ResponseEntity.ok(
CatsgotogedogApiResponse.success("플래너 생성 성공", aiPlannerService.createPlan(userId, request))
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.swyp.catsgotogedog.aiplanner.controller;

import org.springframework.http.ResponseEntity;

import com.swyp.catsgotogedog.aiplanner.request.PlannerRequest;
import com.swyp.catsgotogedog.aiplanner.response.AiplannerResponse;
import com.swyp.catsgotogedog.global.CatsgotogedogApiResponse;
import com.swyp.catsgotogedog.pet.domain.response.PetProfileResponse;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;

@Tag(name = "Ai플래너", description = "AI 플래너 관련 API")
public interface AiplannerControllerSwagger {

@Operation(
summary = "AI 플래너 생성",
description = "AI를 통한 플랜 생성"
)
@SecurityRequirement(name = "bearer-key")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "플랜 생성 성공",
content = @Content(schema = @Schema(implementation = AiplannerResponse.class))),
@ApiResponse(responseCode = "401", description = "인증되지 않은 사용자",
content = @Content(schema = @Schema(implementation = CatsgotogedogApiResponse.class))),
@ApiResponse(responseCode = "404", description = "사용자를 찾을 수 없음",
content = @Content(schema = @Schema(implementation = CatsgotogedogApiResponse.class)))
})
ResponseEntity<CatsgotogedogApiResponse<?>> createAiPlanner(
@Parameter(hidden = true)
String userId,
@RequestBody(description = """
duration은 DAY_TRIP, ONE_NIGHT, TWO_NIGHT 중 하나의 데이터를 입력해야합니다.
mood는 사용자가 입력한 기분 데이터입니다.
""", required = true)
PlannerRequest request
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.swyp.catsgotogedog.aiplanner.domain;

import java.util.List;

import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.annotations.Mapping;
import org.springframework.data.elasticsearch.annotations.Setting;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Document(indexName = "content-embedding")
@Setting(settingPath = "elasticsearch/content-embed-setting.json")
@Mapping(mappingPath = "elasticsearch/content-embed-mapping.json")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Builder
public class ContentEmbedding {

@Id
private Integer contentId;
private int contentTypeId;
private String categoryId;
private int sidoCode;
private int sigunguCode;
private double mapx;
private double mapy;
private String title;

@Field(type = FieldType.Dense_Vector, dims = 1024)
private List<Float> embedding;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.swyp.catsgotogedog.aiplanner.domain;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
public enum TravelDuration {
DAY_TRIP(1, "당일치기"),
ONE_NIGHT(2, "1박2일"),
TWO_NIGHT(3, "2박3일");

private final int days;
private final String description;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.swyp.catsgotogedog.aiplanner.repository;

import java.util.List;

import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

import com.swyp.catsgotogedog.aiplanner.domain.ContentEmbedding;

public interface ContentEmbeddingRepository extends ElasticsearchRepository<ContentEmbedding, Integer> {
List<ContentEmbedding> findByContentIdIn(List<Integer> contentIds);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.swyp.catsgotogedog.aiplanner.request;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.swyp.catsgotogedog.aiplanner.domain.TravelDuration;

import jakarta.validation.Valid;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Valid
public class PlannerRequest {

@JsonProperty
@NotNull(message = "여행 기간은 필수입니다.")
private TravelDuration duration;

@JsonProperty
@NotNull(message = "대분류 여행 지역은 필수입니다.")
@Min(value = 1, message = "올바른 시도 코드를 입력해주세요.")
private Integer sidoCode;

@JsonProperty
@NotBlank(message = "현재 기분은 필수입니다.")
@Size(max = 50, message = "기분 설명은 50자 이하로 입력해주세요")
private String mood;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.swyp.catsgotogedog.aiplanner.response;

import java.util.List;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Setter
@Builder
public class AiplannerResponse {
private List<DayPlan> dayPlans;

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class DayPlan {
private Integer day;
private List<ContentInfo> dayContents;
}

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class ContentInfo {
private Integer contentId;
private String title;
private String categoryId;
private String addr1;
private String addr2;
private String image;
private String thumbImage;
private double mapx;
private double mapy;
private String rest;
}
}
Loading