StoreManagementView.vue services/api.js edit
This commit is contained in:
parent
bfc4d600e2
commit
ccdc2c91bb
BIN
public/images/menu-placeholder.png
Normal file
BIN
public/images/menu-placeholder.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 96 KiB |
@ -1,4 +1,5 @@
|
||||
//* src/services/api.js - 수정된 API URL 설정
|
||||
//* src/services/api.js - 수정된 버전 (createImageApiInstance 함수 추가)
|
||||
|
||||
import axios from 'axios'
|
||||
|
||||
// 런타임 환경 설정에서 API URL 가져오기
|
||||
@ -11,10 +12,9 @@ const getApiUrls = () => {
|
||||
STORE_URL: config.STORE_URL || 'http://localhost:8082/api/store',
|
||||
CONTENT_URL: config.CONTENT_URL || 'http://localhost:8083/api/content',
|
||||
MENU_URL: config.MENU_URL || 'http://localhost:8082/api/menu',
|
||||
// ⚠️ 수정: 매출 API는 store 서비스 (포트 8082)
|
||||
SALES_URL: config.SALES_URL || 'http://localhost:8082/api/sales',
|
||||
// ⚠️ 수정: 추천 API는 ai-recommend 서비스 (포트 8084)
|
||||
RECOMMEND_URL: config.RECOMMEND_URL || 'http://localhost:8084/api/recommendations'
|
||||
RECOMMEND_URL: config.RECOMMEND_URL || 'http://localhost:8084/api/recommendations',
|
||||
IMAGE_URL: config.IMAGE_URL || 'http://localhost:8082/api/images'
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,7 +37,6 @@ const createApiInstance = (baseURL) => {
|
||||
config.headers.Authorization = `Bearer ${token}`
|
||||
}
|
||||
|
||||
// ⚠️ 추가: 요청 로깅 (개발 환경에서만)
|
||||
if (import.meta.env.DEV) {
|
||||
console.log(`🌐 [API_REQ] ${config.method?.toUpperCase()} ${config.baseURL}${config.url}`)
|
||||
}
|
||||
@ -52,14 +51,12 @@ const createApiInstance = (baseURL) => {
|
||||
// 응답 인터셉터 - 토큰 갱신 및 에러 처리
|
||||
instance.interceptors.response.use(
|
||||
(response) => {
|
||||
// ⚠️ 추가: 응답 로깅 (개발 환경에서만)
|
||||
if (import.meta.env.DEV) {
|
||||
console.log(`✅ [API_RES] ${response.status} ${response.config?.method?.toUpperCase()} ${response.config?.url}`)
|
||||
}
|
||||
return response
|
||||
},
|
||||
async (error) => {
|
||||
// ⚠️ 추가: 에러 로깅 (개발 환경에서만)
|
||||
if (import.meta.env.DEV) {
|
||||
console.error(`❌ [API_ERR] ${error.response?.status || 'Network'} ${error.config?.method?.toUpperCase()} ${error.config?.url}`, error.response?.data)
|
||||
}
|
||||
@ -102,10 +99,155 @@ const createApiInstance = (baseURL) => {
|
||||
return instance
|
||||
}
|
||||
|
||||
// ✅ 이미지 업로드 전용 API 인스턴스 생성 함수 추가
|
||||
const createImageApiInstance = (baseURL) => {
|
||||
const instance = axios.create({
|
||||
baseURL,
|
||||
timeout: 60000, // 이미지 업로드는 시간이 더 걸릴 수 있음
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
},
|
||||
})
|
||||
|
||||
// 요청 인터셉터 - JWT 토큰 자동 추가
|
||||
instance.interceptors.request.use(
|
||||
(config) => {
|
||||
const token = localStorage.getItem('accessToken')
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`
|
||||
}
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
console.log(`🌐 [IMG_REQ] ${config.method?.toUpperCase()} ${config.baseURL}${config.url}`)
|
||||
console.log('FormData 포함:', config.data instanceof FormData)
|
||||
}
|
||||
|
||||
return config
|
||||
},
|
||||
(error) => {
|
||||
return Promise.reject(error)
|
||||
},
|
||||
)
|
||||
|
||||
// 응답 인터셉터
|
||||
instance.interceptors.response.use(
|
||||
(response) => {
|
||||
if (import.meta.env.DEV) {
|
||||
console.log(`✅ [IMG_RES] ${response.status} ${response.config?.method?.toUpperCase()} ${response.config?.url}`)
|
||||
}
|
||||
return response
|
||||
},
|
||||
async (error) => {
|
||||
if (import.meta.env.DEV) {
|
||||
console.error(`❌ [IMG_ERR] ${error.response?.status || 'Network'} ${error.config?.method?.toUpperCase()} ${error.config?.url}`, error.response?.data)
|
||||
}
|
||||
|
||||
// 토큰 갱신 로직은 기존과 동일
|
||||
const originalRequest = error.config
|
||||
if (error.response?.status === 401 && !originalRequest._retry) {
|
||||
originalRequest._retry = true
|
||||
try {
|
||||
const refreshToken = localStorage.getItem('refreshToken')
|
||||
if (refreshToken) {
|
||||
const refreshResponse = await axios.post(`${getApiUrls().AUTH_URL}/refresh`, {
|
||||
refreshToken,
|
||||
})
|
||||
const { accessToken, refreshToken: newRefreshToken } = refreshResponse.data.data
|
||||
localStorage.setItem('accessToken', accessToken)
|
||||
localStorage.setItem('refreshToken', newRefreshToken)
|
||||
originalRequest.headers.Authorization = `Bearer ${accessToken}`
|
||||
return instance(originalRequest)
|
||||
}
|
||||
} catch (refreshError) {
|
||||
localStorage.removeItem('accessToken')
|
||||
localStorage.removeItem('refreshToken')
|
||||
localStorage.removeItem('userInfo')
|
||||
window.location.href = '/login'
|
||||
}
|
||||
}
|
||||
return Promise.reject(error)
|
||||
},
|
||||
)
|
||||
|
||||
return instance
|
||||
}
|
||||
|
||||
// ✅ 메뉴 이미지 업로드 전용 API 인스턴스 생성 함수 추가
|
||||
const createMenuImageApiInstance = (baseURL) => {
|
||||
const instance = axios.create({
|
||||
baseURL,
|
||||
timeout: 60000, // 이미지 업로드는 시간이 더 걸릴 수 있음
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
},
|
||||
})
|
||||
|
||||
// 요청 인터셉터 - JWT 토큰 자동 추가
|
||||
instance.interceptors.request.use(
|
||||
(config) => {
|
||||
const token = localStorage.getItem('accessToken')
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`
|
||||
}
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
console.log(`🌐 [MENU_IMG_REQ] ${config.method?.toUpperCase()} ${config.baseURL}${config.url}`)
|
||||
console.log('FormData 포함:', config.data instanceof FormData)
|
||||
}
|
||||
|
||||
return config
|
||||
},
|
||||
(error) => {
|
||||
return Promise.reject(error)
|
||||
},
|
||||
)
|
||||
|
||||
// 응답 인터셉터
|
||||
instance.interceptors.response.use(
|
||||
(response) => {
|
||||
if (import.meta.env.DEV) {
|
||||
console.log(`✅ [MENU_IMG_RES] ${response.status} ${response.config?.method?.toUpperCase()} ${response.config?.url}`)
|
||||
}
|
||||
return response
|
||||
},
|
||||
async (error) => {
|
||||
if (import.meta.env.DEV) {
|
||||
console.error(`❌ [MENU_IMG_ERR] ${error.response?.status || 'Network'} ${error.config?.method?.toUpperCase()} ${error.config?.url}`, error.response?.data)
|
||||
}
|
||||
|
||||
// 토큰 갱신 로직은 기존과 동일
|
||||
const originalRequest = error.config
|
||||
if (error.response?.status === 401 && !originalRequest._retry) {
|
||||
originalRequest._retry = true
|
||||
try {
|
||||
const refreshToken = localStorage.getItem('refreshToken')
|
||||
if (refreshToken) {
|
||||
const refreshResponse = await axios.post(`${getApiUrls().AUTH_URL}/refresh`, {
|
||||
refreshToken,
|
||||
})
|
||||
const { accessToken, refreshToken: newRefreshToken } = refreshResponse.data.data
|
||||
localStorage.setItem('accessToken', accessToken)
|
||||
localStorage.setItem('refreshToken', newRefreshToken)
|
||||
originalRequest.headers.Authorization = `Bearer ${accessToken}`
|
||||
return instance(originalRequest)
|
||||
}
|
||||
} catch (refreshError) {
|
||||
localStorage.removeItem('accessToken')
|
||||
localStorage.removeItem('refreshToken')
|
||||
localStorage.removeItem('userInfo')
|
||||
window.location.href = '/login'
|
||||
}
|
||||
}
|
||||
return Promise.reject(error)
|
||||
},
|
||||
)
|
||||
|
||||
return instance
|
||||
}
|
||||
|
||||
// API 인스턴스들 생성
|
||||
const apiUrls = getApiUrls()
|
||||
|
||||
// ⚠️ 추가: API URL 확인 로깅 (개발 환경에서만)
|
||||
if (import.meta.env.DEV) {
|
||||
console.log('🔧 [API_CONFIG] API URLs 설정:', apiUrls)
|
||||
}
|
||||
@ -115,8 +257,11 @@ export const authApi = createApiInstance(apiUrls.AUTH_URL)
|
||||
export const storeApi = createApiInstance(apiUrls.STORE_URL)
|
||||
export const contentApi = createApiInstance(apiUrls.CONTENT_URL)
|
||||
export const menuApi = createApiInstance(apiUrls.MENU_URL)
|
||||
export const menuImageApi = createMenuImageApiInstance(apiUrls.MENU_URL) // ✅ 추가
|
||||
export const salesApi = createApiInstance(apiUrls.SALES_URL)
|
||||
export const recommendApi = createApiInstance(apiUrls.RECOMMEND_URL)
|
||||
export const imageApi = createApiInstance(apiUrls.IMAGE_URL)
|
||||
export const apiWithImage = imageApi // 별칭 (기존 코드 호환성)
|
||||
|
||||
// 기본 API 인스턴스 (Gateway URL 사용)
|
||||
export const api = createApiInstance(apiUrls.GATEWAY_URL)
|
||||
@ -185,7 +330,7 @@ export const formatSuccessResponse = (data, message = '요청이 성공적으로
|
||||
}
|
||||
}
|
||||
|
||||
// ⚠️ 추가: API 상태 확인 함수
|
||||
// API 상태 확인 함수
|
||||
export const checkApiHealth = async () => {
|
||||
const results = {}
|
||||
|
||||
@ -214,11 +359,14 @@ export const checkApiHealth = async () => {
|
||||
return results
|
||||
}
|
||||
|
||||
// ⚠️ 추가: 개발 환경에서 전역 노출
|
||||
// 개발 환경에서 전역 노출
|
||||
if (import.meta.env.DEV) {
|
||||
window.__api_debug__ = {
|
||||
urls: apiUrls,
|
||||
instances: { memberApi, authApi, storeApi, contentApi, menuApi, salesApi, recommendApi },
|
||||
instances: {
|
||||
memberApi, authApi, storeApi, contentApi, menuApi, menuImageApi,
|
||||
salesApi, recommendApi, imageApi
|
||||
},
|
||||
checkHealth: checkApiHealth
|
||||
}
|
||||
console.log('🔧 [DEBUG] API 인스턴스가 window.__api_debug__에 노출됨')
|
||||
|
||||
@ -79,80 +79,88 @@ export const useStoreStore = defineStore('store', {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 메뉴 목록 조회 - 실제 API 연동 (매장 ID 필요) - ✅ ID 필드 보장
|
||||
*/
|
||||
async fetchMenus() {
|
||||
console.log('=== Store 스토어: 메뉴 목록 조회 시작 ===')
|
||||
// src/store/index.js에서 fetchMenus 부분만 수정
|
||||
|
||||
/**
|
||||
* 메뉴 목록 조회 - 실제 API 연동 (매장 ID 필요) - ✅ 이미지 필드 매핑 수정
|
||||
*/
|
||||
async fetchMenus() {
|
||||
console.log('=== Store 스토어: 메뉴 목록 조회 시작 ===')
|
||||
|
||||
try {
|
||||
// 매장 정보에서 storeId 가져오기
|
||||
const storeId = this.storeInfo?.storeId
|
||||
if (!storeId) {
|
||||
console.warn('매장 ID가 없습니다. 매장 정보를 먼저 조회해주세요.')
|
||||
return { success: false, message: '매장 정보가 필요합니다', data: [] }
|
||||
}
|
||||
|
||||
// 메뉴 서비스 임포트
|
||||
const { menuService } = await import('@/services/menu')
|
||||
|
||||
console.log('메뉴 목록 API 호출, 매장 ID:', storeId)
|
||||
const result = await menuService.getMenus(storeId)
|
||||
|
||||
console.log('=== Store 스토어: 메뉴 API 응답 분석 ===')
|
||||
console.log('Result:', result)
|
||||
console.log('Result.success:', result.success)
|
||||
console.log('Result.data:', result.data)
|
||||
console.log('Result.message:', result.message)
|
||||
|
||||
if (result.success && result.data) {
|
||||
// ✅ 백엔드 MenuResponse의 필드명에 맞게 매핑 수정
|
||||
const menusWithId = (result.data || []).map(menu => {
|
||||
// ID 필드가 확실히 있도록 보장
|
||||
const menuId = menu.menuId || menu.id
|
||||
|
||||
if (!menuId) {
|
||||
console.warn('⚠️ 메뉴 ID가 없는 항목 발견:', menu)
|
||||
}
|
||||
|
||||
console.log('메뉴 원본 데이터:', menu) // 디버깅용
|
||||
|
||||
return {
|
||||
...menu,
|
||||
id: menuId, // ✅ id 필드 확실히 설정
|
||||
menuId: menuId, // ✅ menuId 필드도 설정
|
||||
// 기타 필드들 보장
|
||||
menuName: menu.menuName || menu.name || '이름 없음',
|
||||
category: menu.category || '기타',
|
||||
price: menu.price || 0,
|
||||
description: menu.description || '',
|
||||
available: menu.available !== undefined ? menu.available : true,
|
||||
recommended: menu.recommended !== undefined ? menu.recommended : false,
|
||||
// ✅ 이미지 필드 수정: 백엔드는 'image' 필드 사용
|
||||
imageUrl: menu.image || menu.imageUrl || '/images/menu-placeholder.png',
|
||||
image: menu.image || menu.imageUrl, // 백엔드 호환성
|
||||
createdAt: menu.createdAt,
|
||||
updatedAt: menu.updatedAt
|
||||
}
|
||||
})
|
||||
|
||||
try {
|
||||
// 매장 정보에서 storeId 가져오기
|
||||
const storeId = this.storeInfo?.storeId
|
||||
if (!storeId) {
|
||||
console.warn('매장 ID가 없습니다. 매장 정보를 먼저 조회해주세요.')
|
||||
return { success: false, message: '매장 정보가 필요합니다', data: [] }
|
||||
}
|
||||
|
||||
// 메뉴 서비스 임포트
|
||||
const { menuService } = await import('@/services/menu')
|
||||
|
||||
console.log('메뉴 목록 API 호출, 매장 ID:', storeId)
|
||||
const result = await menuService.getMenus(storeId)
|
||||
|
||||
console.log('=== Store 스토어: 메뉴 API 응답 분석 ===')
|
||||
console.log('Result:', result)
|
||||
console.log('Result.success:', result.success)
|
||||
console.log('Result.data:', result.data)
|
||||
console.log('Result.message:', result.message)
|
||||
|
||||
if (result.success && result.data) {
|
||||
// ✅ 메뉴 데이터 ID 필드 보장 처리
|
||||
const menusWithId = (result.data || []).map(menu => {
|
||||
// ID 필드가 확실히 있도록 보장
|
||||
const menuId = menu.menuId || menu.id
|
||||
|
||||
if (!menuId) {
|
||||
console.warn('⚠️ 메뉴 ID가 없는 항목 발견:', menu)
|
||||
}
|
||||
|
||||
return {
|
||||
...menu,
|
||||
id: menuId, // ✅ id 필드 확실히 설정
|
||||
menuId: menuId, // ✅ menuId 필드도 설정
|
||||
// 기타 필드들 보장
|
||||
menuName: menu.menuName || menu.name || '이름 없음',
|
||||
category: menu.category || '기타',
|
||||
price: menu.price || 0,
|
||||
description: menu.description || '',
|
||||
available: menu.available !== undefined ? menu.available : true,
|
||||
recommended: menu.recommended !== undefined ? menu.recommended : false,
|
||||
imageUrl: menu.imageUrl || '/images/menu-placeholder.png'
|
||||
}
|
||||
})
|
||||
|
||||
// 메뉴 목록이 있는 경우
|
||||
console.log('✅ 메뉴 목록 설정 (ID 보장됨):', menusWithId)
|
||||
this.menus = menusWithId
|
||||
return { success: true, data: menusWithId }
|
||||
} else {
|
||||
// 메뉴가 없거나 조회 실패한 경우
|
||||
console.log('⚠️ 메뉴 목록 없음 또는 조회 실패')
|
||||
this.menus = []
|
||||
|
||||
if (result.message === '등록된 메뉴가 없습니다') {
|
||||
return { success: false, message: '등록된 메뉴가 없습니다', data: [] }
|
||||
} else {
|
||||
return { success: false, message: result.message || '메뉴 목록 조회에 실패했습니다', data: [] }
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('=== Store 스토어: 메뉴 목록 조회 실패 ===')
|
||||
console.error('Error:', error)
|
||||
|
||||
this.menus = []
|
||||
return { success: false, message: error.message || '메뉴 목록을 불러오는데 실패했습니다', data: [] }
|
||||
// 메뉴 목록이 있는 경우
|
||||
console.log('✅ 메뉴 목록 설정 (이미지 필드 매핑 완료):', menusWithId)
|
||||
this.menus = menusWithId
|
||||
return { success: true, data: menusWithId }
|
||||
} else {
|
||||
// 메뉴가 없거나 조회 실패한 경우
|
||||
console.log('⚠️ 메뉴 목록 없음 또는 조회 실패')
|
||||
this.menus = []
|
||||
|
||||
if (result.message === '등록된 메뉴가 없습니다') {
|
||||
return { success: false, message: '등록된 메뉴가 없습니다', data: [] }
|
||||
} else {
|
||||
return { success: false, message: result.message || '메뉴 목록 조회에 실패했습니다', data: [] }
|
||||
}
|
||||
},
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('=== Store 스토어: 메뉴 목록 조회 실패 ===')
|
||||
console.error('Error:', error)
|
||||
|
||||
this.menus = []
|
||||
return { success: false, message: error.message || '메뉴 목록을 불러오는데 실패했습니다', data: [] }
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 매장 등록
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user