diff --git a/src/backend/chat_server/src/main/java/com/jootalkpia/chat_server/controller/ChatController.java b/src/backend/chat_server/src/main/java/com/jootalkpia/chat_server/controller/ChatController.java index d0369472..8936b7ec 100644 --- a/src/backend/chat_server/src/main/java/com/jootalkpia/chat_server/controller/ChatController.java +++ b/src/backend/chat_server/src/main/java/com/jootalkpia/chat_server/controller/ChatController.java @@ -1,14 +1,13 @@ package com.jootalkpia.chat_server.controller; import com.jootalkpia.chat_server.dto.messgaeDto.ChatMessageRequest; -import com.jootalkpia.chat_server.dto.ChatMessageToKafka; -import com.jootalkpia.chat_server.dto.messgaeDto.CommonResponse; import com.jootalkpia.chat_server.service.ChatService; -import com.jootalkpia.chat_server.service.KafkaProducer; -import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.messaging.handler.annotation.DestinationVariable; import org.springframework.messaging.handler.annotation.MessageMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @RestController @@ -21,4 +20,9 @@ public class ChatController { public void sendMessage(ChatMessageRequest request, @DestinationVariable Long channelId) { chatService.processChatMessage(request, channelId); } + + @PostMapping("/chat/{channelId}") + public void sendMessageApi(@RequestBody ChatMessageRequest request, @PathVariable Long channelId) { + chatService.processChatMessage(request, channelId); + } } diff --git a/src/backend/chat_server/src/main/java/com/jootalkpia/chat_server/domain/ReplyThread.java b/src/backend/chat_server/src/main/java/com/jootalkpia/chat_server/domain/ReplyThread.java deleted file mode 100644 index 7cad25df..00000000 --- a/src/backend/chat_server/src/main/java/com/jootalkpia/chat_server/domain/ReplyThread.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.jootalkpia.chat_server.domain; - -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.Table; -import lombok.Getter; - -@Entity -@Table(name = "ReplyThreads") -@Getter -public class ReplyThread extends BaseTimeEntity { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long ReplyThreadId; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "thread_id") - private Thread thread; -} \ No newline at end of file diff --git a/src/backend/chat_server/src/main/java/com/jootalkpia/chat_server/domain/Thread.java b/src/backend/chat_server/src/main/java/com/jootalkpia/chat_server/domain/Thread.java deleted file mode 100644 index 504fb3a0..00000000 --- a/src/backend/chat_server/src/main/java/com/jootalkpia/chat_server/domain/Thread.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.jootalkpia.chat_server.domain; - -import jakarta.persistence.CascadeType; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.OneToMany; -import jakarta.persistence.Table; -import java.util.ArrayList; -import java.util.List; -import lombok.Getter; - -@Entity -@Table(name = "Threads") -@Getter -public class Thread extends BaseTimeEntity { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long threadId; - - @OneToMany(mappedBy = "thread", cascade = CascadeType.ALL, orphanRemoval = true) - private List replyThreads = new ArrayList<>(); -} diff --git a/src/backend/chat_server/src/main/java/com/jootalkpia/chat_server/repository/jpa/ThreadRepository.java b/src/backend/chat_server/src/main/java/com/jootalkpia/chat_server/repository/jpa/ThreadRepository.java deleted file mode 100644 index b697b685..00000000 --- a/src/backend/chat_server/src/main/java/com/jootalkpia/chat_server/repository/jpa/ThreadRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.jootalkpia.chat_server.repository.jpa; - -import com.jootalkpia.chat_server.domain.Thread; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -@Repository -public interface ThreadRepository extends JpaRepository { -} diff --git a/src/backend/chat_server/src/main/java/com/jootalkpia/chat_server/service/ChatService.java b/src/backend/chat_server/src/main/java/com/jootalkpia/chat_server/service/ChatService.java index 5b8037bc..ddfdd5ec 100644 --- a/src/backend/chat_server/src/main/java/com/jootalkpia/chat_server/service/ChatService.java +++ b/src/backend/chat_server/src/main/java/com/jootalkpia/chat_server/service/ChatService.java @@ -1,7 +1,6 @@ package com.jootalkpia.chat_server.service; import com.jootalkpia.chat_server.domain.Files; -import com.jootalkpia.chat_server.domain.Thread; import com.jootalkpia.chat_server.domain.User; import com.jootalkpia.chat_server.dto.ChatMessageToKafka; import com.jootalkpia.chat_server.dto.messgaeDto.ChatMessageRequest; @@ -11,10 +10,13 @@ import com.jootalkpia.chat_server.dto.messgaeDto.TextResponse; import com.jootalkpia.chat_server.dto.messgaeDto.VideoResponse; import com.jootalkpia.chat_server.repository.jpa.FileRepository; -import com.jootalkpia.chat_server.repository.jpa.ThreadRepository; import com.jootalkpia.chat_server.repository.jpa.UserRepository; import com.jootalkpia.chat_server.util.DateTimeUtil; +import com.jootalkpia.chat_server.util.SnowflakeIdGenerator; import jakarta.transaction.Transactional; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; import java.util.ArrayList; import java.util.List; import lombok.RequiredArgsConstructor; @@ -28,7 +30,7 @@ public class ChatService { private final UserRepository userRepository; private final FileRepository fileRepository; - private final ThreadRepository threadRepository; + private final SnowflakeIdGenerator snowflakeIdGenerator; @Transactional public void processChatMessage(ChatMessageRequest request, Long channelId) { @@ -41,13 +43,17 @@ public void processChatMessage(ChatMessageRequest request, Long channelId) { private CommonResponse createCommonData(Long userId, Long channelId){ User user = userRepository.findByUserId(userId); - Thread thread = new Thread(); // todo:transaction처리 , 예외처리 필요 - threadRepository.save(thread); + + Long threadId = snowflakeIdGenerator.nextId(); + + LocalDateTime now = Instant.ofEpochMilli(System.currentTimeMillis()) + .atZone(ZoneId.systemDefault()) + .toLocalDateTime(); return CommonResponse.builder() .channelId(channelId) - .threadId(thread.getThreadId()) - .threadDateTime(DateTimeUtil.formatDateTime(thread.getCreatedAt())) + .threadId(threadId) + .threadDateTime(DateTimeUtil.formatDateTime(now)) .userId(user.getUserId()) .userNickname(user.getNickname()) .userProfileImage(user.getProfileImage()) diff --git a/src/backend/chat_server/src/main/java/com/jootalkpia/chat_server/util/SnowflakeIdGenerator.java b/src/backend/chat_server/src/main/java/com/jootalkpia/chat_server/util/SnowflakeIdGenerator.java new file mode 100644 index 00000000..0d03b98a --- /dev/null +++ b/src/backend/chat_server/src/main/java/com/jootalkpia/chat_server/util/SnowflakeIdGenerator.java @@ -0,0 +1,85 @@ +package com.jootalkpia.chat_server.util; + +import org.springframework.stereotype.Component; + +import java.net.NetworkInterface; +import java.security.SecureRandom; +import java.util.Enumeration; + +@Component +public class SnowflakeIdGenerator { + private static final long EPOCH = 1672531200000L; // 기준이 되는 Epoch 시간 (2023-01-01 00:00:00 기준) + private static final long WORKER_ID_BITS = 5L; + private static final long DATACENTER_ID_BITS = 5L; + private static final long SEQUENCE_BITS = 12L; + + private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS); + private static final long MAX_DATACENTER_ID = ~(-1L << DATACENTER_ID_BITS); + + private static final long WORKER_ID_SHIFT = SEQUENCE_BITS; + private static final long DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS; + private static final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATACENTER_ID_BITS; + private static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS); + + private long workerId; + private long datacenterId; + private long sequence = 0L; + private long lastTimestamp = -1L; + + public SnowflakeIdGenerator() { + this.workerId = getWorkerId(); + this.datacenterId = getDataCenterId(); + } + + public synchronized long nextId() { + long timestamp = System.currentTimeMillis(); + + if (timestamp < lastTimestamp) { + throw new RuntimeException("generate snowflake id"); + } + + if (lastTimestamp == timestamp) { + sequence = (sequence + 1) & SEQUENCE_MASK; + if (sequence == 0) { + timestamp = waitNextMillis(lastTimestamp); + } + } else { + sequence = 0L; + } + + lastTimestamp = timestamp; + + return ((timestamp - EPOCH) << TIMESTAMP_LEFT_SHIFT) + | (datacenterId << DATACENTER_ID_SHIFT) + | (workerId << WORKER_ID_SHIFT) + | sequence; + } + + private long waitNextMillis(long lastTimestamp) { + long timestamp = System.currentTimeMillis(); + while (timestamp <= lastTimestamp) { + timestamp = System.currentTimeMillis(); + } + return timestamp; + } + + private long getWorkerId() { + try { + Enumeration e = NetworkInterface.getNetworkInterfaces(); + while (e.hasMoreElements()) { + NetworkInterface ni = e.nextElement(); + byte[] mac = ni.getHardwareAddress(); + if (mac != null) { + return ((mac[mac.length - 1] & 0xFF) % (MAX_WORKER_ID + 1)); + } + } + } catch (Exception ex) { + return new SecureRandom().nextInt((int) MAX_WORKER_ID + 1); + } + return new SecureRandom().nextInt((int) MAX_WORKER_ID + 1); + } + + private long getDataCenterId() { + return new SecureRandom().nextInt((int) MAX_DATACENTER_ID + 1); + } +} \ No newline at end of file