Skip to content
Open
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

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,122 +1,76 @@
package com.assu.server.domain.certification.component;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


import org.springframework.stereotype.Component;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
//
// @Slf4j // ⭐️ SLF4j 로그 사용을 위해 추가
// @Component
// public class CertificationSessionManager {
// private final Map<Long, Set<Long>> sessionUserMap = new ConcurrentHashMap<>();
//
// public void openSession(Long sessionId) {
// sessionUserMap.put(sessionId, ConcurrentHashMap.newKeySet());
// // ⭐️ 로그 추가
// log.info("✅ New certification session opened. SessionID: {}", sessionId);
// }
//
// public void addUserToSession(Long sessionId, Long userId) {
// Set<Long> users = sessionUserMap.computeIfAbsent(sessionId, k -> {
// log.warn("Attempted to add user to a non-existent session. Creating new set for SessionID: {}", k);
// return ConcurrentHashMap.newKeySet();
// });
//
// boolean isAdded = users.add(userId);
//
// // ⭐️ 요청하신 멤버 추가 확인 로그
// if (isAdded) {
// log.info("👤 User added to session. SessionID: {}, UserID: {}. Current participants: {}",
// sessionId, userId, users.size());
// } else {
// log.info("👤 User already in session. SessionID: {}, UserID: {}. Current participants: {}",
// sessionId, userId, users.size());
// }
// }
//
// public int getCurrentUserCount(Long sessionId) {
// return sessionUserMap.getOrDefault(sessionId, Set.of()).size();
// }
//
// public boolean hasUser(Long sessionId, Long userId) {
// return sessionUserMap.getOrDefault(sessionId, Set.of()).contains(userId);
// }
//
// public List<Long> snapshotUserIds(Long sessionId) {
// return List.copyOf(sessionUserMap.getOrDefault(sessionId, Set.of()));
// }
//
//
//
// public void removeSession(Long sessionId) {
// sessionUserMap.remove(sessionId);
// // ⭐️ 로그 추가
// log.info("❌ Certification session removed. SessionID: {}", sessionId);
// }
// }

@Component
@RequiredArgsConstructor
public class CertificationSessionManager {

// RedisTemplate을 주입받습니다.
private final StringRedisTemplate redisTemplate;
private static final String ID_COUNTER_KEY = "certification:id:seq";
private static final String SESSION_INIT_MARKER = "STATUS_OPEN";

// 세션 ID를 위한 KEY를 만드는 헬퍼 메서드
private String getKey(Long sessionId) {
return "certification:session:" + sessionId;
}

public void openSession(Long sessionId) {
String key = getKey(sessionId);
// 세션을 연다는 것은 키를 만드는 것과 같습니다.
// addUserToSession에서 자동으로 키가 생성되므로 이 메서드는 비워두거나,
// 만료 시간 설정 등 초기화 로직을 넣을 수 있습니다.
// 예: 10분 후 만료
private String getInfoKey(Long sessionId) {
return "certification:info:" + sessionId;
}

public Long openSession(Long storeId, Integer peopleNumber) {
Long newSessionId = redisTemplate.opsForValue().increment(ID_COUNTER_KEY);
if (newSessionId == null) throw new RuntimeException("ID 생성 실패");

String key = getKey(newSessionId);
String infoKey = getInfoKey(newSessionId);

redisTemplate.opsForSet().add(key, SESSION_INIT_MARKER);
redisTemplate.expire(key, 10, TimeUnit.MINUTES);

redisTemplate.opsForHash().put(infoKey, "storeId", String.valueOf(storeId));
redisTemplate.opsForHash().put(infoKey, "peopleNumber", String.valueOf(peopleNumber));
redisTemplate.expire(infoKey, 10, TimeUnit.MINUTES);

return newSessionId;
}

public void addUserToSession(Long sessionId, Long userId) {
String key = getKey(sessionId);
// Redis의 Set 자료구조에 userId를 추가합니다.
redisTemplate.opsForSet().add(key, String.valueOf(userId));
public boolean exists(Long sessionId) {
return Boolean.TRUE.equals(redisTemplate.hasKey(getKey(sessionId)));
}

public int getCurrentUserCount(Long sessionId) {
String key = getKey(sessionId);
// Redis Set의 크기를 반환합니다.
Long size = redisTemplate.opsForSet().size(key);
return size != null ? size.intValue() : 0;
public String getSessionInfo(Long sessionId, String field) {
return (String) redisTemplate.opsForHash().get(getInfoKey(sessionId), field);
}

public void addUserToSession(Long sessionId, Long userId) {
redisTemplate.opsForSet().add(getKey(sessionId), String.valueOf(userId));
}

public boolean hasUser(Long sessionId, Long userId) {
String key = getKey(sessionId);
// Redis Set에 해당 멤버가 있는지 확인합니다.
return redisTemplate.opsForSet().isMember(key, String.valueOf(userId));
return redisTemplate.opsForSet().isMember(getKey(sessionId), String.valueOf(userId));
}

public List<Long> snapshotUserIds(Long sessionId) {
String key = getKey(sessionId);
// Redis Set의 모든 멤버를 가져옵니다.
Set<String> members = redisTemplate.opsForSet().members(key);
if (members == null) {
return List.of();
}
Set<String> members = redisTemplate.opsForSet().members(getKey(sessionId));
if (members == null) return List.of();

return members.stream()
.filter(m -> !SESSION_INIT_MARKER.equals(m))
.map(Long::valueOf)
.collect(Collectors.toList());
}

public void removeSession(Long sessionId) {
String key = getKey(sessionId);
// 세션 키 자체를 삭제합니다.
redisTemplate.delete(key);
redisTemplate.delete(getKey(sessionId));
redisTemplate.delete(getInfoKey(sessionId));
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -35,41 +35,6 @@ public Message<?> preSend(Message<?> message, MessageChannel channel) {
log.info("Authentication set: {}", authentication);
}
}

return message;
}

// @Override
// public Message<?> preSend(Message<?> message, MessageChannel channel) {
// StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
// log.info("StompCommand: {}", accessor.getCommand()); // StompCommand 로그 추가
//
// if (StompCommand.CONNECT.equals(accessor.getCommand())) {
// log.info("CONNECT command received.");
// // 프론트에서 connect 시 Authorization 헤더 넣어야 함
// String authHeader = accessor.getFirstNativeHeader("Authorization");
// log.info("Authorization Header: {}", authHeader); // Authorization 헤더 로그 추가
//
// if (authHeader != null && authHeader.startsWith("Bearer ")) {
// String token = jwtUtil.getTokenFromHeader(authHeader);
// log.info("Extracted Token: {}", token); // 추출된 토큰 로그 추가
//
// // JwtUtil 이용해서 Authentication 복원
// Authentication authentication = jwtUtil.getAuthentication(token);
// log.info("Authentication restored: {}", authentication); // 복원된 인증 정보 로그 추가
//
// // WebSocket 세션에 Authentication(UserPrincipal) 저장
// accessor.setUser(authentication);
// log.info("User principal set on accessor.");
// } else {
// log.warn("Authorization header is missing or not in Bearer format.");
// }
// } else if (StompCommand.SEND.equals(accessor.getCommand())) {
// // SEND 명령어에 대한 로그 추가 (메시지 전송 시)
// Object payload = message.getPayload();
// log.info("SEND command received. Destination: {}, Payload: {}", accessor.getDestination(), payload);
// }
//
// return message;
// }
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,19 @@
package com.assu.server.domain.certification.controller;

import java.time.LocalDateTime;

import org.springframework.http.ResponseEntity;
import org.springframework.messaging.handler.annotation.MessageMapping;
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.RestController;

import com.assu.server.domain.certification.dto.CertificationRequestDTO;
import com.assu.server.domain.certification.dto.CertificationGroupRequestDTO;
import com.assu.server.domain.certification.dto.CertificationPersonalRequestDTO;
import com.assu.server.domain.certification.dto.CertificationResponseDTO;
import com.assu.server.domain.certification.service.CertificationService;
import com.assu.server.domain.member.entity.Member;
import com.assu.server.domain.member.repository.MemberRepository;
import com.assu.server.global.apiPayload.BaseResponse;
import com.assu.server.global.apiPayload.code.status.ErrorStatus;
import com.assu.server.global.apiPayload.code.status.SuccessStatus;
import com.assu.server.global.exception.GeneralException;
import com.assu.server.global.util.PrincipalDetails;


import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
Expand All @@ -31,7 +24,6 @@
public class CertificationController {

private final CertificationService certificationService;
private final MemberRepository memberRepository; // 지금은 그냥 임시 데이터 하드 코딩이라 여기에 둔거여

@PostMapping("/certification/session")
@Operation(
Expand All @@ -50,33 +42,40 @@ public class CertificationController {
"\n**Response:**\n" +
" - 성공 시 201(Created)와 sessionId, adminId 반환"
)
public ResponseEntity<BaseResponse<CertificationResponseDTO.getSessionIdResponse>> getSessionId(
public ResponseEntity<BaseResponse<CertificationResponseDTO>> getSessionId(
@AuthenticationPrincipal PrincipalDetails pd,
@RequestBody CertificationRequestDTO.groupRequest dto
@RequestBody CertificationGroupRequestDTO dto
) {

CertificationResponseDTO.getSessionIdResponse result = certificationService.getSessionId(dto, pd.getMember());
CertificationResponseDTO result = certificationService.getSessionId(dto, pd.getMember());

return ResponseEntity.ok(BaseResponse.onSuccess(SuccessStatus.GROUP_SESSION_CREATE, result));
}

// @MessageMapping("/certify")
// @Operation(summary = "그룹 세션 인증 api", description = "그룹에 대한 세션 인증 요청을 보냅니다.")
// public ResponseEntity<BaseResponse<Void>> certifyGroup(
// CertificationRequestDTO.groupSessionRequest dto , PrincipalDetails pd
//
// ) {
// certificationService.handleCertification(dto, pd.getMember());
//
// return ResponseEntity.ok(BaseResponse.onSuccess(SuccessStatus.GROUP_CERTIFICATION_SUCCESS, null));
// }

@PostMapping("/certification/personal")
@Operation(summary = "개인 인증 api", description = "사실 크게 필요없는데, 제휴 내역 통계를 위해 데이터를 post하는 api 입니다. "
+ "가게 별 제휴를 조회하고 people값이 null 인 제휴를 선택한 경우 그룹 인증 대신 요청하는 api 입니다.")
@Operation(
summary = "개인 인증 요청 API",
description =
"# [v1.0 (2025-09-09)](https://clumsy-seeder-416.notion.site/2471197c19ed80fd9a8dcc43fb938a5d?source=copy_link)\n" +
"- 개인 단위 인증을 위한 API입니다.\n" +
"- 그룹 인증이 아닌 경우, 통계 및 제휴 이력 적재를 목적으로 사용됩니다.\n" +
"- 가게별 제휴 조회 시 `people` 값이 null 인 경우 호출됩니다.\n" +
"\n**Request Body:**\n" +
" - `storeId` (Long, required): 인증이 발생한 스토어 ID\n" +
" - `adminId` (Long, required): 인증을 요청한 관리자 ID\n" +
" - `tableNumber` (Integer, required): 인증이 발생한 테이블 번호\n" +
"\n**Authentication:**\n" +
" - 로그인된 사용자 인증 정보 필요 (`@AuthenticationPrincipal`)\n" +
"\n**Processing:**\n" +
" - 전달받은 정보 기반으로 개인 인증 이력 저장\n" +
" - 세션 생성은 하지 않음\n" +
"\n**Response:**\n" +
" - 성공 시 200(OK)\n" +
" - 성공 메시지 반환"
)
public ResponseEntity<BaseResponse<String>> personalCertification(
@AuthenticationPrincipal PrincipalDetails pd,
@RequestBody CertificationRequestDTO.personalRequest dto
@RequestBody CertificationPersonalRequestDTO dto
) {
certificationService.certificatePersonal(dto, pd.getMember());

Expand Down
Loading
Loading