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 @@ -6,10 +6,10 @@
import com.back.domain.user.entity.User;
import com.back.domain.user.service.GuestService;
import com.back.domain.user.service.UserService;
import com.back.global.common.ApiResponse;
import com.back.global.security.CustomUserDetails;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
Expand Down Expand Up @@ -39,14 +39,13 @@ public class UserAuthController {
private final GuestService guestService;

@PostMapping("/signup")
public ResponseEntity<ApiResponse<UserResponse>> signup(@Valid @RequestBody SignupRequest req){
public ResponseEntity<UserResponse> signup(@Valid @RequestBody SignupRequest req){
User saved = userService.signup(req);
return ResponseEntity.status(HttpStatus.CREATED)
.body(ApiResponse.success(UserResponse.from(saved), "성공적으로 생성되었습니다."));
return ResponseEntity.status(HttpStatus.CREATED).body(UserResponse.from(saved));
}

@PostMapping("/login")
public ResponseEntity<ApiResponse<UserResponse>> login(
public ResponseEntity<UserResponse> login(
@Valid @RequestBody LoginRequest req,
HttpServletRequest request,
HttpServletResponse response)
Expand All @@ -60,29 +59,33 @@ public ResponseEntity<ApiResponse<UserResponse>> login(
.saveContext(SecurityContextHolder.getContext(), request, response);

CustomUserDetails cud = (CustomUserDetails) auth.getPrincipal();
return ResponseEntity.ok(ApiResponse.success(UserResponse.from(cud.getUser()), "로그인 성공"));
return ResponseEntity.ok(UserResponse.from(cud.getUser()));
}

@PostMapping("/guest")
public ResponseEntity<ApiResponse<UserResponse>> guestLogin(HttpServletRequest request, HttpServletResponse response){
public ResponseEntity<UserResponse> guestLogin(HttpServletRequest request, HttpServletResponse response){
User savedGuest = guestService.createAndSaveGuest();

CustomUserDetails cud = new CustomUserDetails(savedGuest);
Authentication auth = new UsernamePasswordAuthenticationToken(cud, null, cud.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(auth);

request.getSession(true);
HttpSession session = request.getSession(true);

session.setMaxInactiveInterval(600);
session.setAttribute("guestId", savedGuest.getId());

new HttpSessionSecurityContextRepository()
.saveContext(SecurityContextHolder.getContext(), request, response);

return ResponseEntity.ok(ApiResponse.success(UserResponse.from(savedGuest), "게스트 로그인 성공"));
return ResponseEntity.ok(UserResponse.from(savedGuest));
}

@GetMapping("/me")
public ResponseEntity<ApiResponse<UserResponse>> me(@AuthenticationPrincipal CustomUserDetails cud) {
public ResponseEntity<UserResponse> me(@AuthenticationPrincipal CustomUserDetails cud) {
if (cud == null) {
return ResponseEntity.ok(ApiResponse.success(null, "anonymous"));
return ResponseEntity.ok(null); // 익명 사용자
}
return ResponseEntity.ok(ApiResponse.success(UserResponse.from(cud.getUser()), "authenticated"));
return ResponseEntity.ok(UserResponse.from(cud.getUser()));
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.back.domain.user.controller;

import com.back.domain.user.dto.UserInfoRequest;
import com.back.domain.user.dto.UserInfoResponse;
import com.back.domain.user.service.UserInfoService;
import com.back.global.security.CustomUserDetails;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/v1/users-info")
@RequiredArgsConstructor
public class UserInfoController {
private final UserInfoService userInfoService;

@GetMapping
public ResponseEntity<UserInfoResponse> getMyInfo(@AuthenticationPrincipal CustomUserDetails principal) {
return ResponseEntity.ok(userInfoService.getMyInfo(principal.getId()));
}

@PostMapping
public ResponseEntity<UserInfoResponse> createMyInfo(@AuthenticationPrincipal CustomUserDetails principal,
@Valid @RequestBody UserInfoRequest req) {
return ResponseEntity.ok(userInfoService.createMyInfo(principal.getId(), req));
}

@PutMapping
public ResponseEntity<UserInfoResponse> updateMyInfo(@AuthenticationPrincipal CustomUserDetails principal,
@Valid @RequestBody UserInfoRequest req) {
return ResponseEntity.ok(userInfoService.updateMyInfo(principal.getId(), req));
}
}
19 changes: 19 additions & 0 deletions back/src/main/java/com/back/domain/user/dto/UserInfoRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.back.domain.user.dto;


import com.back.domain.user.entity.Gender;
import com.back.domain.user.entity.Mbti;

import java.time.LocalDateTime;

public record UserInfoRequest(
String username,
LocalDateTime birthdayAt,
Gender gender,
Mbti mbti,
String beliefs,
Integer lifeSatis,
Integer relationship,
Integer workLifeBal,
Integer riskAvoid
) {}
39 changes: 39 additions & 0 deletions back/src/main/java/com/back/domain/user/dto/UserInfoResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.back.domain.user.dto;

import com.back.domain.user.entity.Gender;
import com.back.domain.user.entity.Mbti;
import com.back.domain.user.entity.User;

import java.time.LocalDateTime;

public record UserInfoResponse(
String email,
String username,
String nickname,
LocalDateTime birthdayAt,
Gender gender,
Mbti mbti,
String beliefs,
Integer lifeSatis,
Integer relationship,
Integer workLifeBal,
Integer riskAvoid,
LocalDateTime updatedAt
) {
public static UserInfoResponse from(User user) {
return new UserInfoResponse(
user.getEmail(),
user.getUsername(),
user.getNickname(),
user.getBirthdayAt(),
user.getGender(),
user.getMbti(),
user.getBeliefs(),
user.getLifeSatis(),
user.getRelationship(),
user.getWorkLifeBal(),
user.getRiskAvoid(),
user.getUpdatedAt()
);
}
}
35 changes: 31 additions & 4 deletions back/src/main/java/com/back/domain/user/entity/User.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
package com.back.domain.user.entity;

import com.back.domain.node.entity.BaseLine;
import com.back.domain.node.entity.BaseNode;
import com.back.domain.node.entity.DecisionLine;
import com.back.domain.node.entity.DecisionNode;
import com.back.domain.scenario.entity.Scenario;
import com.back.global.baseentity.BaseEntity;
import jakarta.persistence.*;
import lombok.*;
import org.springframework.data.annotation.LastModifiedDate;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
* 사용자 정보를 저장하는 엔티티.
Expand Down Expand Up @@ -47,17 +54,37 @@ public class User extends BaseEntity {

private String beliefs;

private String lifeSatis;
private Integer lifeSatis;

private String relationship;
private Integer relationship;

private String workLifeBal;
private Integer workLifeBal;

private String riskAvoid;
private Integer riskAvoid;

@Enumerated(EnumType.STRING)
private AuthProvider authProvider;

@LastModifiedDate
private LocalDateTime updatedAt;

@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
@Builder.Default
private List<BaseLine> baseLines = new ArrayList<>();

@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
@Builder.Default
private List<BaseNode> baseNodes = new ArrayList<>();

@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
@Builder.Default
private List<DecisionLine> decisionLines = new ArrayList<>();

@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
@Builder.Default
private List<DecisionNode> decisionNodes = new ArrayList<>();

@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
@Builder.Default
private List<Scenario> scenarios = new ArrayList<>();
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.back.domain.user.repository;

import com.back.domain.user.entity.Role;
import com.back.domain.user.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.time.LocalDateTime;
import java.util.Optional;

/**
Expand All @@ -15,4 +17,6 @@ public interface UserRepository extends JpaRepository<User, Long> {

boolean existsByEmail(String email);
boolean existsByNickname(String nickname);

int deleteByRoleAndCreatedDateBefore(Role role, LocalDateTime cutoff);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.back.domain.user.service;

import com.back.domain.user.dto.UserInfoRequest;
import com.back.domain.user.dto.UserInfoResponse;
import com.back.domain.user.entity.User;
import com.back.domain.user.repository.UserRepository;
import jakarta.persistence.EntityNotFoundException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class UserInfoService {

private final UserRepository userRepository;

@Transactional(readOnly = true)
public UserInfoResponse getMyInfo(Long userId) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new EntityNotFoundException("User not found: " + userId));
return UserInfoResponse.from(user);
}

@Transactional
public UserInfoResponse createMyInfo(Long userId, UserInfoRequest req) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new EntityNotFoundException("User not found: " + userId));

applyPatch(user, req);

return UserInfoResponse.from(user);
}

@Transactional
public UserInfoResponse updateMyInfo(Long userId, UserInfoRequest req) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new EntityNotFoundException("User not found: " + userId));

applyPatch(user, req);

return UserInfoResponse.from(user);
}

private void applyPatch(User user, UserInfoRequest req) {
if (req.username() != null) user.setUsername(req.username());
if (req.birthdayAt() != null) user.setBirthdayAt(req.birthdayAt());
if (req.gender() != null) user.setGender(req.gender());
if (req.mbti() != null) user.setMbti(req.mbti());
if (req.beliefs() != null) user.setBeliefs(req.beliefs());
if (req.lifeSatis() != null) user.setLifeSatis(req.lifeSatis());
if (req.relationship() != null) user.setRelationship(req.relationship());
if (req.workLifeBal() != null) user.setWorkLifeBal(req.workLifeBal());
if (req.riskAvoid() != null) user.setRiskAvoid(req.riskAvoid());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.back.global.scheduler;

import com.back.domain.user.entity.Role;
import com.back.domain.user.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;

/**
* 게스트 계정 자동 정리 스케줄러
* 세션 리스너가 놓친 게스트 계정을 주기적으로 정리하는 안전망
* 실행 시점: 매일 새벽 3시
* 삭제 기준: 생성된 지 24시간이 지난 게스트 계정
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class GuestCleanupScheduler {

private final UserRepository userRepository;

// 매일 새벽 3시에 실행
@Scheduled(cron = "0 0 3 * * *")
@Transactional
public void cleanExpiredGuests() {
LocalDateTime cutoff = LocalDateTime.now().minusHours(24);
int deleted = userRepository.deleteByRoleAndCreatedDateBefore(Role.GUEST, cutoff);
if (deleted > 0) {
log.info("배치 작업으로 만료된 게스트 {}건 삭제", deleted);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class CsrfCookieFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain)
throws ServletException, IOException {
CsrfToken token = (CsrfToken) req.getAttribute(CsrfToken.class.getName());
CsrfToken token = (CsrfToken) req.getAttribute("_csrf");
if (token != null) token.getToken();
chain.doFilter(req, res);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + user.getRole().name()));
}

public Long getId() { return user.getId(); }

@Override
public String getPassword() {
return user.getPassword();
Expand Down
Loading