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
Expand Up @@ -12,6 +12,7 @@
import com.haru.api.global.annotation.AuthUser;
import com.haru.api.global.annotation.AuthWorkspace;
import com.haru.api.global.apiPayload.code.status.SuccessStatus;
import com.haru.api.global.util.HashIdUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
Expand All @@ -32,6 +33,7 @@ public class MoodTrackerController {

// 읽기 전용
private final MoodTrackerQueryService moodTrackerQueryService;
private final HashIdUtil hashIdUtil;


@GetMapping("/workspaces/{workspaceId}")
Expand Down Expand Up @@ -137,59 +139,54 @@ public ApiResponse<Void> sendMoodTrackerSurveyLink(

@PostMapping("/{mood-tracker-hashed-Id}/answer")
@Operation(
summary = "분위기 트래커 설문 답변 제출 API",
description = "# [v1.1 (2025-08-05)](https://www.notion.so/2265da7802c580c58d36e73639e41291) 해당 ID의 분위기 트래커 설문 답변을 제출합니다."
summary = "분위기 트래커 설문 답변 제출 API (비인증)",
description = "# [v1.2 (2025-08-19)](https://www.notion.so/2265da7802c580c58d36e73639e41291) 해당 ID의 분위기 트래커 설문 답변을 제출합니다."
)
@Parameters({
@Parameter(name = "mood-tracker-hashed-Id", description = "해시된 16자 분위기 트래커 ID (Path Variable)", required = true)
})
public ApiResponse<Void> submitMoodTrackerSurveyAnswers(
@PathVariable("mood-tracker-hashed-Id") String moodTrackerHashedId,
@Valid @RequestBody MoodTrackerRequestDTO.SurveyAnswerList request,
@Parameter(hidden = true) @AuthMoodTracker MoodTracker moodTracker
@Valid @RequestBody MoodTrackerRequestDTO.SurveyAnswerList request
) {

moodTrackerCommandService.submitSurveyAnswers(moodTracker, request);
moodTrackerCommandService.submitSurveyAnswers(hashIdUtil.decode(moodTrackerHashedId), request);

return ApiResponse.of(SuccessStatus.MOOD_TRACKER_ANSWER_SUBMIT, null);

}

@GetMapping("/{mood-tracker-hashed-Id}")
@GetMapping("/{mood-tracker-hashed-Id}/bases")
@Operation(
summary = "분위기 트래커 설문 팀분위기 베이스 정보 조회 API",
description = "# [v1.0 (2025-08-19)](https://www.notion.so/2545da7802c580dd9742d971d3a4bc08?source=copy_link) 분위기 트래커(moodTrackerId)에 대한 베이스 정보를 조회합니다."
summary = "분위기 트래커 설문 팀분위기 베이스 정보 조회 API (비인증)",
description = "# [v1.1 (2025-08-19)](https://www.notion.so/2545da7802c580dd9742d971d3a4bc08?source=copy_link) 분위기 트래커(moodTrackerId)에 대한 베이스 정보를 조회합니다."
)
@Parameters({
@Parameter(name = "mood-tracker-hashed-Id", description = "분위기 트래커 ID (Hashed, Path Variable)", required = true)
})
public ApiResponse<MoodTrackerResponseDTO.BaseResult> getMoodTrackerBaseResult(
@PathVariable(name = "mood-tracker-hashed-Id") String moodTrackerHashedId,
@Parameter(hidden = true) @AuthUser User user,
@Parameter(hidden = true) @AuthMoodTracker MoodTracker moodTracker
@PathVariable(name = "mood-tracker-hashed-Id") String moodTrackerHashedId
) {

MoodTrackerResponseDTO.BaseResult result = moodTrackerQueryService.getBaseResult(user, moodTracker);
MoodTrackerResponseDTO.BaseResult result = moodTrackerQueryService.getBaseResult(hashIdUtil.decode(moodTrackerHashedId));

return ApiResponse.onSuccess(result);

}

@GetMapping("/{mood-tracker-hashed-Id}/questions")
@Operation(
summary = "분위기 트래커 설문 문항 조회 API",
description = "# [v1.2 (2025-08-05)](https://www.notion.so/2295da7802c580dbb88aee8687b69e32) 분위기 트래커(moodTrackerId)에 해당하는 설문 문항들을 조회합니다."
summary = "분위기 트래커 설문 문항 조회 API (비인증)",
description = "# [v1.3 (2025-08-19)](https://www.notion.so/2295da7802c580dbb88aee8687b69e32) 분위기 트래커(moodTrackerId)에 해당하는 설문 문항들을 조회합니다."
)
@Parameters({
@Parameter(name = "mood-tracker-hashed-Id", description = "분위기 트래커 ID (Hashed, Path Variable)", required = true)
})
public ApiResponse<MoodTrackerResponseDTO.QuestionResult> getMoodTrackerQuestionResult(
@PathVariable(name = "mood-tracker-hashed-Id") String moodTrackerHashedId,
@Parameter(hidden = true) @AuthUser User user,
@Parameter(hidden = true) @AuthMoodTracker MoodTracker moodTracker
@PathVariable(name = "mood-tracker-hashed-Id") String moodTrackerHashedId
) {

MoodTrackerResponseDTO.QuestionResult result = moodTrackerQueryService.getQuestionResult(user, moodTracker);
MoodTrackerResponseDTO.QuestionResult result = moodTrackerQueryService.getQuestionResult(hashIdUtil.decode(moodTrackerHashedId));

return ApiResponse.onSuccess(result);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ void sendSurveyLink(
MoodTracker moodTracker
);
void submitSurveyAnswers(
MoodTracker moodTracker,
Long moodTrackerId,
MoodTrackerRequestDTO.SurveyAnswerList request
);
MoodTrackerResponseDTO.ReportDownLoadLinkResponse getDownloadLink(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,11 +180,14 @@ public void sendSurveyLink(
@Override
@Transactional
public void submitSurveyAnswers(
MoodTracker moodTracker,
Long moodTrackerId,
MoodTrackerRequestDTO.SurveyAnswerList request
) {
MoodTracker foundMoodTracker = moodTrackerRepository.findById(moodTrackerId)
.orElseThrow(() -> new MoodTrackerHandler(ErrorStatus.MOOD_TRACKER_NOT_FOUND));

// 마감일 이후이면 답변 불가능
if(moodTracker.getDueDate().isBefore(LocalDateTime.now())){
if(foundMoodTracker.getDueDate().isBefore(LocalDateTime.now())){
throw new MoodTrackerHandler(ErrorStatus.MOOD_TRACKER_FINISHED);
}

Expand All @@ -193,7 +196,7 @@ public void submitSurveyAnswers(
List<CheckboxChoiceAnswer> checkboxChoiceAnswers = new ArrayList<>();

// 전체 질문을 미리 조회 및 맵에 캐싱
List<SurveyQuestion> foundQuestions = surveyQuestionRepository.findAllByMoodTrackerId(moodTracker.getId());
List<SurveyQuestion> foundQuestions = surveyQuestionRepository.findAllByMoodTrackerId(foundMoodTracker.getId());
Map<Long, SurveyQuestion> questionMap = foundQuestions.stream()
.collect(Collectors.toMap(SurveyQuestion::getId, q -> q));

Expand Down Expand Up @@ -259,7 +262,7 @@ public void submitSurveyAnswers(
subjectiveAnswerRepository.saveAll(subjectiveAnswers);

// 답변자 수 증가
moodTrackerRepository.addRespondentsNum(moodTracker.getId());
moodTrackerRepository.addRespondentsNum(foundMoodTracker.getId());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
public interface MoodTrackerQueryService {
MoodTrackerResponseDTO.PreviewList getPreviewList(User user, Workspace workspace);

MoodTrackerResponseDTO.BaseResult getBaseResult(User user, MoodTracker moodTracker);
MoodTrackerResponseDTO.BaseResult getBaseResult(Long moodTrackerId);

MoodTrackerResponseDTO.QuestionResult getQuestionResult(User user, MoodTracker moodTracker);
MoodTrackerResponseDTO.QuestionResult getQuestionResult(Long moodTrackerId);

MoodTrackerResponseDTO.ReportResult getReportResult(User user, MoodTracker moodTracker);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,38 +66,22 @@ public MoodTrackerResponseDTO.PreviewList getPreviewList(User user, Workspace wo

@Override
@Transactional(readOnly = true)
@TrackLastOpened
public MoodTrackerResponseDTO.BaseResult getBaseResult(User user, MoodTracker moodTracker) {

// 워크스페이스 권한 조회
UserWorkspace foundUserWorkspace = userWorkspaceRepository.findByWorkspaceIdAndUserId(
moodTracker.getWorkspace().getId(), user.getId()
).orElseThrow(() -> new UserWorkspaceHandler(ErrorStatus.USER_WORKSPACE_NOT_FOUND));

// 권한 검증
boolean hasAccess =
// 워크스페이스 생성자
foundUserWorkspace.getAuth().equals(Auth.ADMIN)
// 해당 MoodTracker 생성자
|| moodTracker.getCreator().getId().equals(user.getId())
// 공개된 설문
|| moodTracker.getVisibility().equals(MoodTrackerVisibility.PUBLIC);

if (!hasAccess) {
throw new MoodTrackerHandler(ErrorStatus.MOOD_TRACKER_ACCESS_DENIED);
}
public MoodTrackerResponseDTO.BaseResult getBaseResult(Long moodTrackerId) {
MoodTracker foundMoodTracker = moodTrackerRepository.findById(moodTrackerId)
.orElseThrow(() -> new MoodTrackerHandler(ErrorStatus.MOOD_TRACKER_NOT_FOUND));

return MoodTrackerConverter.toBaseResultDTO(moodTracker, hashIdUtil);
return MoodTrackerConverter.toBaseResultDTO(foundMoodTracker, hashIdUtil);
}

@Override
@Transactional(readOnly = true)
@TrackLastOpened
public MoodTrackerResponseDTO.QuestionResult getQuestionResult(User user, MoodTracker moodTracker) {
public MoodTrackerResponseDTO.QuestionResult getQuestionResult(Long moodTrackerId) {
MoodTracker foundMoodTracker = moodTrackerRepository.findById(moodTrackerId)
.orElseThrow(() -> new MoodTrackerHandler(ErrorStatus.MOOD_TRACKER_NOT_FOUND));

List<SurveyQuestion> questionList = surveyQuestionRepository.findAllByMoodTrackerId(moodTracker.getId());
List<SurveyQuestion> questionList = surveyQuestionRepository.findAllByMoodTrackerId(foundMoodTracker.getId());

return MoodTrackerConverter.toQuestionResultDTO(moodTracker, questionList, hashIdUtil);
return MoodTrackerConverter.toQuestionResultDTO(foundMoodTracker, questionList, hashIdUtil);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
"/api/v1/sns/oauth/callback",
"/api/v1/users/signup/same",
"/favicon.ico",
"/api/v1//mood-trackers/*/questions",
"/api/v1//mood-trackers/*/responses"
"/api/v1/mood-trackers/*/questions",
"/api/v1/mood-trackers/*/answer",
"/api/v1/mood-trackers/*/bases"
};
private final JwtUtils jwtUtils;
private final RedisTemplate<String, String> redisTemplate;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ private SecurityUtil() { }
public static Long getCurrentUserId() {
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

if (authentication == null || authentication.getName() == null) {
throw new RuntimeException("Security Context 에 인증 정보가 없습니다.");
if (authentication == null || !authentication.isAuthenticated()
|| authentication.getPrincipal().equals("anonymousUser")) {
return null; // 또는 Optional.empty()
}

return Long.parseLong(authentication.getName());
Expand Down