From f3939b46264d26effe7af36b18baae1cdb2cdc0f Mon Sep 17 00:00:00 2001 From: thdwoqor Date: Thu, 26 Dec 2024 00:00:29 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20job=20skill=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/config/WebSocketConfig.java | 15 ++++-- .../interceptor/PathMatcherInterceptor.java | 47 ++++++++++--------- .../WebsocketPlayerArgumentResolver.java | 35 ++++++++++++++ .../mafiatogether/job/ui/JobController.java | 31 +++++++----- src/main/resources/static/app.js | 20 ++++++++ src/main/resources/static/index.html | 9 +++- 6 files changed, 118 insertions(+), 39 deletions(-) create mode 100644 src/main/java/mafia/mafiatogether/common/resolver/WebsocketPlayerArgumentResolver.java diff --git a/src/main/java/mafia/mafiatogether/common/config/WebSocketConfig.java b/src/main/java/mafia/mafiatogether/common/config/WebSocketConfig.java index 57a7d0e..c5435ba 100644 --- a/src/main/java/mafia/mafiatogether/common/config/WebSocketConfig.java +++ b/src/main/java/mafia/mafiatogether/common/config/WebSocketConfig.java @@ -1,10 +1,12 @@ package mafia.mafiatogether.common.config; - +import java.util.List; import lombok.RequiredArgsConstructor; +import mafia.mafiatogether.chat.ui.WebsocketPlayerArgumentResolver; import mafia.mafiatogether.common.interceptor.ChatInterceptor; import mafia.mafiatogether.common.interceptor.PathMatcherInterceptor; import org.springframework.context.annotation.Configuration; +import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver; import org.springframework.messaging.simp.config.ChannelRegistration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; import org.springframework.messaging.simp.stomp.StompCommand; @@ -30,6 +32,11 @@ public void registerStompEndpoints(StompEndpointRegistry registry) { ); } + @Override + public void addArgumentResolvers(final List argumentResolvers) { + argumentResolvers.add(new WebsocketPlayerArgumentResolver()); + } + @Override public void configureMessageBroker(MessageBrokerRegistry registry) { registry.setApplicationDestinationPrefixes("/pub"); @@ -40,9 +47,9 @@ public void configureMessageBroker(MessageBrokerRegistry registry) { public void configureClientInboundChannel(ChannelRegistration registry) { registry.interceptors( new PathMatcherInterceptor(chatInterceptor) - .includePathPattern("/chat/**", StompCommand.SUBSCRIBE) - .includePathPattern("/chat/**", StompCommand.DISCONNECT) - .includePathPattern("/chat/**", StompCommand.MESSAGE) + .includePathPattern("/sub/chat/**", StompCommand.SUBSCRIBE) + .includePathPattern("/pub/chat/**", StompCommand.MESSAGE) + .includePathPattern("/pub/chat/**", StompCommand.SEND) ); } diff --git a/src/main/java/mafia/mafiatogether/common/interceptor/PathMatcherInterceptor.java b/src/main/java/mafia/mafiatogether/common/interceptor/PathMatcherInterceptor.java index 83fed57..74c4b09 100644 --- a/src/main/java/mafia/mafiatogether/common/interceptor/PathMatcherInterceptor.java +++ b/src/main/java/mafia/mafiatogether/common/interceptor/PathMatcherInterceptor.java @@ -15,55 +15,56 @@ public class PathMatcherInterceptor implements ChannelInterceptor { private final ChannelInterceptor channelInterceptor; private final PathMatcher pathMatcher; - private final List includePathPattern; - private final List excludePathPattern; + private final List includePathPatterns; + private final List excludePathPatterns; - public PathMatcherInterceptor( - final ChannelInterceptor channelInterceptor - ) { + public PathMatcherInterceptor(final ChannelInterceptor channelInterceptor) { this.channelInterceptor = channelInterceptor; this.pathMatcher = new AntPathMatcher(); - this.includePathPattern = new ArrayList<>(); - this.excludePathPattern = new ArrayList<>(); + this.includePathPatterns = new ArrayList<>(); + this.excludePathPatterns = new ArrayList<>(); } - @Override public Message preSend(final Message message, final MessageChannel channel) { StompHeaderAccessor headerAccessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class); - if ( + if (headerAccessor != null && headerAccessor.getDestination() != null && - notIncludedPath(headerAccessor.getDestination(), headerAccessor.getCommand()) - ) { + shouldIntercept(headerAccessor.getDestination(), headerAccessor.getCommand())) { return channelInterceptor.preSend(message, channel); } return ChannelInterceptor.super.preSend(message, channel); } - private boolean notIncludedPath(String destination, StompCommand command) { - boolean excludePattern = excludePathPattern.stream() - .anyMatch(stompMapping -> anyMatchPathPattern(destination, command, stompMapping)); + private boolean shouldIntercept(String destination, StompCommand command) { + boolean isExcluded = excludePathPatterns.stream() + .anyMatch(stompMapping -> matchesPathAndCommand(destination, command, stompMapping)); + + boolean isIncluded = includePathPatterns.stream() + .anyMatch(stompMapping -> matchesPathAndCommand(destination, command, stompMapping)); - boolean includePattern = includePathPattern.stream() - .anyMatch(stompMapping -> anyMatchPathPattern(destination, command, stompMapping)); + System.out.println("##"); + System.out.println("##"); + System.out.println(isExcluded); + System.out.println(isIncluded); - return excludePattern || !includePattern; + return isIncluded && !isExcluded; } - private boolean anyMatchPathPattern(String destination, StompCommand command, StompMapping stompMapping) { - return pathMatcher.match(destination, stompMapping.destination()) && + private boolean matchesPathAndCommand(String destination, StompCommand command, StompMapping stompMapping) { + return pathMatcher.match(stompMapping.destination(), destination) && stompMapping.command() == command; } - public PathMatcherInterceptor includePathPattern(String targetPath, StompCommand pathMethod) { - this.includePathPattern.add(new StompMapping(targetPath, pathMethod)); + public PathMatcherInterceptor includePathPattern(String targetPath, StompCommand command) { + this.includePathPatterns.add(new StompMapping(targetPath, command)); return this; } - public PathMatcherInterceptor excludePathPattern(String targetPath, StompCommand pathMethod) { - this.excludePathPattern.add(new StompMapping(targetPath, pathMethod)); + public PathMatcherInterceptor excludePathPattern(String targetPath, StompCommand command) { + this.excludePathPatterns.add(new StompMapping(targetPath, command)); return this; } diff --git a/src/main/java/mafia/mafiatogether/common/resolver/WebsocketPlayerArgumentResolver.java b/src/main/java/mafia/mafiatogether/common/resolver/WebsocketPlayerArgumentResolver.java new file mode 100644 index 0000000..fe86a1f --- /dev/null +++ b/src/main/java/mafia/mafiatogether/common/resolver/WebsocketPlayerArgumentResolver.java @@ -0,0 +1,35 @@ +package mafia.mafiatogether.chat.ui; + +import mafia.mafiatogether.common.annotation.PlayerInfo; +import mafia.mafiatogether.common.exception.AuthException; +import mafia.mafiatogether.common.exception.ExceptionCode; +import mafia.mafiatogether.common.resolver.PlayerInfoDto; +import mafia.mafiatogether.common.util.AuthExtractor; +import org.springframework.core.MethodParameter; +import org.springframework.messaging.Message; +import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver; +import org.springframework.messaging.simp.SimpMessageHeaderAccessor; + +public class WebsocketPlayerArgumentResolver implements HandlerMethodArgumentResolver { + @Override + public boolean supportsParameter(final MethodParameter parameter) { + return parameter.hasParameterAnnotation(PlayerInfo.class); + } + + @Override + public Object resolveArgument(final MethodParameter parameter, final Message message) throws Exception { + SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.wrap(message); + String authorization = headerAccessor.getFirstNativeHeader("Authorization"); + if (authorization == null) { + throw new AuthException(ExceptionCode.MISSING_AUTHENTICATION_HEADER); + } + if (!authorization.startsWith("Basic")) { + throw new AuthException(ExceptionCode.MISSING_AUTHENTICATION_HEADER); + } + + String[] information = AuthExtractor.extractByAuthorization(authorization); + + return new PlayerInfoDto(information[0], information[1]); + } + +} diff --git a/src/main/java/mafia/mafiatogether/job/ui/JobController.java b/src/main/java/mafia/mafiatogether/job/ui/JobController.java index ea1f086..21c9c54 100644 --- a/src/main/java/mafia/mafiatogether/job/ui/JobController.java +++ b/src/main/java/mafia/mafiatogether/job/ui/JobController.java @@ -1,8 +1,10 @@ package mafia.mafiatogether.job.ui; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.Base64; import lombok.RequiredArgsConstructor; -import mafia.mafiatogether.chat.annotation.SendToChatWithRedis; -import mafia.mafiatogether.chat.domain.Message; import mafia.mafiatogether.common.annotation.PlayerInfo; import mafia.mafiatogether.common.resolver.PlayerInfoDto; import mafia.mafiatogether.job.application.JobService; @@ -10,8 +12,8 @@ import mafia.mafiatogether.job.application.dto.response.JobExecuteAbilityResponse; import mafia.mafiatogether.job.application.dto.response.JobResponse; import mafia.mafiatogether.job.application.dto.response.JobResultResponse; +import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.http.ResponseEntity; -import org.springframework.messaging.handler.annotation.DestinationVariable; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.web.bind.annotation.GetMapping; @@ -25,6 +27,8 @@ @RequestMapping("/jobs") public class JobController { + private final StringRedisTemplate stringRedisTemplate; + private final ObjectMapper objectMapper; private final JobService jobService; @GetMapping("/my") @@ -32,15 +36,20 @@ public ResponseEntity getJob(@PlayerInfo PlayerInfoDto playerInfoDt return ResponseEntity.ok(jobService.getPlayerJob(playerInfoDto.code(), playerInfoDto.name())); } - @MessageMapping("/skill/{code}/{name}") - @SendToChatWithRedis("/sub/mafia/{code}") - public Message executeSkill( - @DestinationVariable("code") String code, - @DestinationVariable("name") String name, + @MessageMapping("/jobs/skill") + public void executeWebSocketSkill( + @PlayerInfo PlayerInfoDto playerInfoDto, @Payload JobExecuteAbilityRequest request - ) { - JobExecuteAbilityResponse response = jobService.executeSkill(code, name, request); - return Message.ofChat(response.job(), response.result()); + ) throws JsonProcessingException { + JobExecuteAbilityResponse response = jobService.executeSkill(playerInfoDto.code(), playerInfoDto.name(), + request); + String auth = Base64.getEncoder() + .encodeToString((playerInfoDto.code() + ":" + playerInfoDto.name()).getBytes()); + + stringRedisTemplate.convertAndSend( + String.format("/sub/job/skill/%s/%s", response.job().toLowerCase(), auth), + objectMapper.writeValueAsString(response) + ); } @PostMapping("/skill") diff --git a/src/main/resources/static/app.js b/src/main/resources/static/app.js index 8f9003d..af95ba3 100644 --- a/src/main/resources/static/app.js +++ b/src/main/resources/static/app.js @@ -8,6 +8,10 @@ stompClient.onConnect = (frame) => { stompClient.subscribe('/sub/chat/aGVsbG86cG93ZXJhc3M=', (greeting) => { showGreeting(JSON.parse(greeting.body)); }); + + stompClient.subscribe('/sub/job/skill/mafia/aGVsbG86cG93ZXJhc3M=', (greeting) => { + showSkill(JSON.parse(greeting.body)); + }); }; stompClient.onWebSocketError = (error) => { @@ -59,11 +63,26 @@ function leaveRoom() { }); } +function executeSkill() { + stompClient.publish({ + destination: "/pub/jobs/skill", + body: JSON.stringify({'target': $("#target").val()}), + headers: { + Authorization: 'Basic aGVsbG86cG93ZXJhc3M=', + }, + }); +} + function showGreeting(message) { console.log('Received: ' + JSON.stringify(message)); $("#greeting").append("" + JSON.stringify(message) + ""); } +function showSkill(message) { + console.log('Received: ' + JSON.stringify(message)); + $("#greeting").append("" + JSON.stringify(message) + ""); +} + $(function () { $("form").on('submit', (e) => e.preventDefault()); $("#connect").click(() => connect()); @@ -71,4 +90,5 @@ $(function () { $("#send").click(() => sendName()); $("#enter").click(() => enterRoom()); $("#leave").click(() => leaveRoom()); + $("#skill").click(() => executeSkill()); }); diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html index 7b86f54..b395080 100644 --- a/src/main/resources/static/index.html +++ b/src/main/resources/static/index.html @@ -34,6 +34,13 @@ +
+
+ + +
+ +
@@ -51,4 +58,4 @@
- \ No newline at end of file +