Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
94aaa41
2025-08-16
Hoody-rj Aug 16, 2025
1ca80d7
2025-08-16
Hoody-rj Aug 16, 2025
ab3f029
add : Requestdto 추가 및 Service의 CRUD추가 및 구현
Hoody-rj Aug 16, 2025
ff40fba
add : 배송 목록 요청 API
Hoody-rj Aug 16, 2025
b8fafa4
add : 네이버 Direction 5 배송 루트 dto 추가
Hoody-rj Aug 16, 2025
c0efea4
add : 네이버 응답 dto 및 요청 dto
Hoody-rj Aug 16, 2025
5155840
Remove application.yml from a git
Hoody-rj Aug 16, 2025
26960f2
add : UserInformationResponseDto 추가(사용자 조회 목적)
Hoody-rj Aug 17, 2025
760ed54
error : 커밋 에러 테스트
Hoody-rj Aug 17, 2025
3df4259
test
Hoody-rj Aug 17, 2025
622dba3
test
Hoody-rj Aug 17, 2025
c8b7778
Merge pull request #2 from TunaForce4/dev
Hoody-rj Aug 17, 2025
ade616c
Merge pull request #6 from TunaForce4/main
Hoody-rj Aug 17, 2025
e4d684c
error : 커밋 에러 테스트
Hoody-rj Aug 17, 2025
decf544
fix : SQL 키 생성 시 UUID 값 반환 예제 검색 후 적용
Hoody-rj Aug 17, 2025
82b069c
Merge remote-tracking branch 'origin/dev' into develope
Hoody-rj Aug 17, 2025
a773304
fix : SQL 키 생성 시 UUID 값 반환 예제 검색 후 적용
Hoody-rj Aug 17, 2025
0ebaebd
fix : MapKeyRequestDto Valid NotNull 선언
Hoody-rj Aug 18, 2025
5b87739
chore : Controller 로직 간결화 및 Service 로직 수정
Hoody-rj Aug 18, 2025
baf02f9
chore : Controller 로직 간결화 및 Service 로직 수정
Hoody-rj Aug 18, 2025
3531ab2
chore : Message dto 이름 변경
Hoody-rj Aug 18, 2025
51be0bc
chore : Message Controller CRUD 작업
Hoody-rj Aug 18, 2025
b82151d
add : hub FeignClient 추가
Hoody-rj Aug 18, 2025
d97f175
delete : ResultData 필요 없는 클래스 제거
Hoody-rj Aug 18, 2025
1afd743
add : naver api 연동시 필요한 dto 추가
Hoody-rj Aug 19, 2025
9f5a3d1
add : naver 연동 dto 및 테스트 모듈 로직 추가
Hoody-rj Aug 19, 2025
008611d
add : Naver 관련 class 추가
Hoody-rj Aug 20, 2025
15f228a
add : 경로 추출 함수 테스트 모듈 추가
Hoody-rj Aug 20, 2025
ec4e160
add : 경로 추출 함수 테스트 모듈 추가
Hoody-rj Aug 20, 2025
39f7bbc
add : 스케쥴러 로직 추가(배송 물품 및 담당자 정보)
Hoody-rj Aug 22, 2025
f3d9098
add : 배송 담당자 배정 로직 추가
Hoody-rj Aug 22, 2025
c660969
fix : 스케쥴러 수정 및 three bound 에러 수정
Hoody-rj Aug 22, 2025
f95a92e
fix : 메세지 앱 문구 수정
Hoody-rj Aug 22, 2025
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
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ bin/
out/
!**/src/main/**/out/
!**/src/test/**/out/

*.yml
/application.yml
SwaggerConfig.java
### NetBeans ###
/nbproject/private/
/nbbuild/
Expand Down
8 changes: 8 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ dependencies {
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.amqp:spring-rabbit-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
implementation 'com.slack.api:slack-api-client:1.25.0'
implementation 'com.google.code.gson:gson:2.8.9'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.5.0'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
}

dependencyManagement {
Expand All @@ -46,6 +50,10 @@ dependencyManagement {
}
}

springBoot {
mainClass = 'com.tunaforce.message.MessageApplication'
}

tasks.named('test') {
useJUnitPlatform()
}
8 changes: 8 additions & 0 deletions src/main/java/com/tunaforce/message/MessageApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,16 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableFeignClients
@EnableDiscoveryClient
@EnableJpaAuditing
@EnableScheduling
public class MessageApplication {

public static void main(String[] args) {
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/com/tunaforce/message/api/ApiException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.tunaforce.message.api;

import lombok.Getter;

@Getter
public class ApiException extends RuntimeException {
private ApiStatus apiStatus;

public ApiException(ApiStatus apiStatus) {
this.apiStatus = apiStatus;
}
}
85 changes: 85 additions & 0 deletions src/main/java/com/tunaforce/message/api/ApiExceptionAdvice.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package com.tunaforce.message.api;

import com.slack.api.methods.SlackApiException;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.resource.NoResourceFoundException;

import java.util.NoSuchElementException;

@RestControllerAdvice
public class ApiExceptionAdvice {
@ExceptionHandler(ApiException.class)
public ResponseEntity<ApiResponse<Object>> customExceptionHandler(HttpServletRequest request, final ApiException e) {
ApiResponse<Object> apiResponse = ApiResponse.<Object>builder()
.status(e.getApiStatus().getStatusCode())
.message(e.getApiStatus().getMessage())
.errors(null)
.build();

return ResponseEntity
.ok(apiResponse);
}

@ExceptionHandler(DataIntegrityViolationException.class)
public ResponseEntity<ApiResponse<Object>> dataIntegrityViolationExceptionHandler(HttpServletRequest request, final DataIntegrityViolationException dataIntegrityViolationException) {
ApiResponse<Object> apiResponse = ApiResponse.<Object>builder()
.status(ApiStatus.REQUIRED_ERROR.getStatusCode())
.message(ApiStatus.REQUIRED_ERROR.getMessage())
.errors(dataIntegrityViolationException.getMessage())
.build();

return ResponseEntity
.ok(apiResponse);
}

@ExceptionHandler(NoResourceFoundException.class)
public ResponseEntity<ApiResponse<Object>> notFoundExceptionHandler(HttpServletRequest request, final NoResourceFoundException noResourceFoundException) {
ApiResponse<Object> apiResponse = ApiResponse.<Object>builder()
.status(ApiStatus.NOT_FOUND.getStatusCode())
.message(ApiStatus.NOT_FOUND.getMessage())
.errors(noResourceFoundException.getMessage())
.build();

return ResponseEntity
.ok(apiResponse);
}
@ExceptionHandler(NoSuchElementException.class)
public ResponseEntity<ApiResponse<Object>> noSuchElementExceptionHandler(HttpServletRequest request, final NoSuchElementException NoSuchElementException) {
ApiResponse<Object> apiResponse = ApiResponse.<Object>builder()
.status(ApiStatus.NOT_FOUND.getStatusCode())
.message(ApiStatus.NOT_FOUND.getMessage())
.errors(NoSuchElementException.getMessage())
.build();

return ResponseEntity
.ok(apiResponse);
}

@ExceptionHandler(SlackApiException.class)
public ResponseEntity<ApiResponse<Object>> slackApiExceptionHandler(HttpServletRequest request, final SlackApiException slackApiException) {
ApiResponse<Object> apiResponse = ApiResponse.<Object>builder()
.status(ApiStatus.NOT_FOUND.getStatusCode())
.message(ApiStatus.NOT_FOUND.getMessage())
.errors(slackApiException.getMessage())
.build();

return ResponseEntity.status(HttpStatus.NOT_FOUND).body(apiResponse);
}

@ExceptionHandler(SlackMessageSendException.class)
public ResponseEntity<ApiResponse<Object>> handleSlackMessageSendException(SlackMessageSendException ex) {
ApiResponse<Object> apiResponse = ApiResponse.<Object>builder()
.status(HttpStatus.INTERNAL_SERVER_ERROR.value()) // 500 상태 코드
.message("Slack 메시지 전송 실패")
.errors(ex.getMessage())
.build();

return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(apiResponse);
}

}
36 changes: 36 additions & 0 deletions src/main/java/com/tunaforce/message/api/ApiResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.tunaforce.message.api;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class ApiResponse<T> {
private int status;
private String message;

@Builder.Default // builder 패턴으로 객체 생성 시 기본 값 지정
private String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));

private T data;
private String errors;

public static <T> ApiResponse<T> success(T data) {
return ApiResponse.<T>builder()
.status(ApiStatus.SUCCESS.getStatusCode())
.message(ApiStatus.SUCCESS.getMessage())
.data(data)
.build();
}

public static <T> ApiResponse<T> error(int status, String message, String errors) {
return ApiResponse.<T>builder()
.status(status)
.message(message)
.errors(errors)
.build();
}
}
20 changes: 20 additions & 0 deletions src/main/java/com/tunaforce/message/api/ApiStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.tunaforce.message.api;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum ApiStatus {
SUCCESS(200, "요청이 성공적으로 완료되었습니다."),
CREATED(201, "리소스가 성공적으로 생성되었습니다."),
BAD_REQUEST(400, "요청이 유효하지 않습니다."),
UNAUTHORIZED(401, "권한이 확인되지 않았습니다."),
FORBIDDEN(403, "접근이 거부되었습니다."),
NOT_FOUND(404, "요청한 리소스를 찾을 수 없습니다."),
REQUIRED_ERROR(422, "필수 값이 누락되었습니다."),
INTERNAL_SERVER_ERROR(500, "서버에서 예기치 못한 오류가 발생했습니다.");

private final int statusCode;
private final String message;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.tunaforce.message.api;

import com.slack.api.methods.SlackApiException;

public class SlackMessageSendException extends RuntimeException {
public SlackMessageSendException(String message) {
super(message);
}
}
52 changes: 52 additions & 0 deletions src/main/java/com/tunaforce/message/cmmn/RouteData.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.tunaforce.message.cmmn;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class RouteData {
//필수 필드만 추가
// 출발지
//출발지(경도,위도)
//<예시> start=127.12345,37.12345
private String start;
// 도착지
//목적지(경도,위도)
//':'로 구분하여 최대 10개의 목적지 입력 가능
//입력한 목적지 중 가장 적은 비용으로 도달할 수 있는 목적지에 대한 경로 조회 <예시> goal=123.45678,34.56789:124.56789,35.67890
private String goal;
//추후 리팩토링 시 추가
//경유지
// private String waypoints;
//경로 조회 옵션
//trafast | tracomfort | traoptimal (기본값) | traavoidtoll | traavoidcaronly
//trafast : 실시간 빠른 길
//tracomfort : 실시간 편한 길
//traoptimal : 실시간 최적
//traavoidtoll : 무료 우선
//traavoidcaronly : 자동차 전용 도로 회피 우선
// private String option;
//차량 타입
//1 (기본값) | 2 | 3 | 4 | 5 | 6
//1 : 1종 소형차 2축 차량(윤폭 279.4 mm 이하 승용차, 소형 승합차, 소형 화물차)
//2 : 2종 2축 차량(윤폭 279.4 mm 초과, 윤거 1,800 mm 이하 중형 승합차, 중형 화물차) 3 : 3종 대형차 2축 차량(윤폭 279.4 mm 초과, 윤거 1,800 mm 초과 대형 승합차, 2축 대형
//화물차)
//4 : 4종 3축 대형 화물차
//5 : 5종 4축 이상 특수 화물차
//6 : 1종 경형 자동차(배기량 1000 cc 미만으로 길이 3.6 m, 너비 1.6 m, 높이 2.0 m 이하)
// private String cartype;
//연료 타입
// asoline (기본값) | highgradegasoline | diesel | lpg
// gasoline : 휘발유
// highgradegasoline : 고급 휘발유
// diesel : 경유
// lpg : LPG
// 유류비 계산에 활용
// private String fueltype;

public String returnQueryUri() {
return "?start=" + this.start + "&goal=" + this.goal;
}

}
117 changes: 117 additions & 0 deletions src/main/java/com/tunaforce/message/cmmn/SlackMsg.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package com.tunaforce.message.cmmn;

import com.slack.api.Slack;
import com.slack.api.methods.SlackApiException;
import com.slack.api.methods.request.chat.ChatPostMessageRequest;
import com.slack.api.methods.request.conversations.ConversationsHistoryRequest;
import com.slack.api.methods.request.conversations.ConversationsOpenRequest;
import com.slack.api.methods.response.chat.ChatPostMessageResponse;
import com.slack.api.methods.response.conversations.ConversationsOpenResponse;
import com.tunaforce.message.api.SlackMessageSendException;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.Collections;

@Slf4j
@Component
public class SlackMsg {

//토큰은 auth 모듈에서 api로 받아 redis에 저장
// @Value("${slack.api.token}")
// private String slackToken;
//userid로 변경하여 auth 모듈에서 불러오기
// @Value("${slack.channel.id}")
// private String channelId;



public void sendMessageChannel(String slackToken, String channelId,String message) {
Slack slack = Slack.getInstance();

try {
ChatPostMessageResponse response = slack.methods().chatPostMessage(req -> req
.token(slackToken)
.channel(channelId)
.text(message));
boolean resultCheck;
if (response.isOk()) {
String timestamp = response.getTs();
log.info("메시지 전송 성공: " + response.getMessage().getText());
log.info("메세지 ID: " + timestamp);

resultCheck = true;
} else {
log.error("메시지 전송 실패: " + response.getError());
resultCheck = false;
}
// return new RequestSlackSend(
// resultCheck,
// message
// );
} catch (SlackApiException | IOException e) {
e.printStackTrace();
// return new RequestSlackSend(
// false,
// "Error : " + e.getMessage() + message
// );
}
}
public void sendDirectMessage(String slackToken,String userId, String message) throws IOException, SlackApiException {
Slack slack = Slack.getInstance();

// 1. 사용자와의 DM 대화방 열기
ConversationsOpenResponse openResponse = slack.methods(slackToken).conversationsOpen(
ConversationsOpenRequest.builder()
.users(Collections.singletonList(userId)) // 수정: List 형태로 전달
.build()
);


if (!openResponse.isOk()) {
throw new SlackMessageSendException("Error opening conversation: " + openResponse.getError());
}
else{
log.info("From. {} \n message has sent : {}",userId ,message);
}

String channelId = openResponse.getChannel().getId();
log.info("testing channelId : {} ", channelId);
// 2. 메시지 전송
ChatPostMessageResponse postResponse = slack.methods(slackToken).chatPostMessage(
ChatPostMessageRequest.builder()
.channel(channelId)
.text(message)
.build()//D09AJSBRB8W
);

if (!postResponse.isOk()) {
throw new RuntimeException("Error sending message: " + postResponse.getError());
}
}


public void fetchHistory(String slackToken, String channelId, Integer brCount) {
var client = Slack.getInstance().methods();
try {
var response = client.conversationsHistory(ConversationsHistoryRequest.builder()
.token(slackToken)
.channel(channelId)
.limit(brCount) // 원하는 메시지 개수 지정
.build());

if (response.isOk() && response.getMessages() != null) {
response.getMessages().forEach(msg ->
System.out.println("[" + msg.getTs() + "] " + msg.getText()));
} else {
System.out.println("Error fetching history: " + response.getError());
}
} catch (IOException | SlackApiException e) {
e.printStackTrace();
}
}
}
Loading