diff --git a/src/services/api.js b/src/services/api.js index b4efbef..9273a97 100644 --- a/src/services/api.js +++ b/src/services/api.js @@ -6,6 +6,7 @@ const getApiUrls = () => { const config = window.__runtime_config__ || {} return { GATEWAY_URL: config.GATEWAY_URL || 'http://20.1.2.3', +<<<<<<< HEAD AUTH_URL: config.AUTH_URL || 'http://localhost:8081/api/auth', MEMBER_URL: config.MEMBER_URL || 'http://localhost:8081/api/member', STORE_URL: config.STORE_URL || 'http://localhost:8082/api/store', @@ -15,6 +16,15 @@ const getApiUrls = () => { SALES_URL: config.SALES_URL || 'http://localhost:8082/api/sales', // ⚠️ 수정: 추천 API는 ai-recommend 서비스 (포트 8084) RECOMMEND_URL: config.RECOMMEND_URL || 'http://localhost:8084/api/recommendations', +======= + AUTH_URL: 'http://localhost:8081/api/auth', + MEMBER_URL: 'http://localhost:8081/api/member', + STORE_URL: config.STORE_URL || 'http://localhost:8082/api/store', + CONTENT_URL: config.CONTENT_URL || 'http://20.1.2.3/api/content', + MENU_URL: config.MENU_URL || 'http://localhost:8082/api/menu', + SALES_URL: config.SALES_URL || 'http://localhost:8082/api/sales', + RECOMMEND_URL: config.RECOMMEND_URL || 'http://20.1.2.3/api/recommendation', +>>>>>>> 87871709f2bbcdab5df004a3954d6ba0af3cadce } } diff --git a/src/services/menu.js b/src/services/menu.js new file mode 100644 index 0000000..d911e78 --- /dev/null +++ b/src/services/menu.js @@ -0,0 +1,210 @@ +// src/services/menu.js - 메뉴 관련 API 서비스 +import { menuApi, handleApiError, formatSuccessResponse } from './api.js' + +/** + * 메뉴 관련 API 서비스 + * 백엔드 Menu Controller와 연동 (포트 8082) + */ +class MenuService { + /** + * 메뉴 목록 조회 + * @param {number} storeId - 매장 ID + * @returns {Promise} 메뉴 목록 + */ + async getMenus(storeId) { + try { + console.log('=== 메뉴 목록 조회 API 호출 ===') + console.log('매장 ID:', storeId) + + if (!storeId) { + throw new Error('매장 ID가 필요합니다') + } + + // GET /api/menu?storeId={storeId} + const response = await menuApi.get('', { + params: { storeId } + }) + + console.log('메뉴 목록 조회 API 응답:', response.data) + + if (response.data && response.data.status === 200) { + return formatSuccessResponse(response.data.data, '메뉴 목록을 조회했습니다.') + } else { + throw new Error(response.data.message || '메뉴 목록을 찾을 수 없습니다.') + } + } catch (error) { + console.error('메뉴 목록 조회 실패:', error) + + // 404 오류 또는 네트워크 오류 시 빈 배열 반환 (개발 중) + if (error.response?.status === 404 || + error.code === 'ECONNREFUSED' || + error.message.includes('Network Error')) { + console.warn('백엔드 미구현 또는 네트워크 오류 - 빈 메뉴 목록 반환') + return formatSuccessResponse([], '메뉴 목록이 비어있습니다.') + } + + return handleApiError(error) + } + } + + /** + * 메뉴 상세 조회 + * @param {number} menuId - 메뉴 ID + * @returns {Promise} 메뉴 상세 정보 + */ + async getMenuDetail(menuId) { + try { + console.log('=== 메뉴 상세 조회 API 호출 ===') + console.log('메뉴 ID:', menuId) + + if (!menuId || menuId === 'undefined') { + throw new Error('올바른 메뉴 ID가 필요합니다') + } + + const numericMenuId = parseInt(menuId) + if (isNaN(numericMenuId)) { + throw new Error('메뉴 ID는 숫자여야 합니다') + } + + // GET /api/menu/{menuId} + const response = await menuApi.get(`/${numericMenuId}`) + + console.log('메뉴 상세 조회 API 응답:', response.data) + + if (response.data && response.data.status === 200) { + return formatSuccessResponse(response.data.data, '메뉴 상세 정보를 조회했습니다.') + } else { + throw new Error(response.data.message || '메뉴를 찾을 수 없습니다.') + } + } catch (error) { + console.error('메뉴 상세 조회 실패:', error) + return handleApiError(error) + } + } + + /** + * 메뉴 등록 + * @param {Object} menuData - 메뉴 정보 + * @returns {Promise} 등록 결과 + */ + async createMenu(menuData) { + try { + console.log('=== 메뉴 등록 API 호출 ===') + console.log('요청 데이터:', menuData) + + const requestData = { + storeId: menuData.storeId, + menuName: menuData.menuName, + category: menuData.category, + price: parseInt(menuData.price) || 0, + description: menuData.description || '' + } + + console.log('백엔드 전송 데이터:', requestData) + + // POST /api/menu/register + const response = await menuApi.post('/register', requestData) + + console.log('메뉴 등록 API 응답:', response.data) + + if (response.data && response.data.status === 200) { + return formatSuccessResponse(response.data.data, '메뉴가 성공적으로 등록되었습니다.') + } else { + throw new Error(response.data.message || '메뉴 등록에 실패했습니다.') + } + } catch (error) { + console.error('메뉴 등록 실패:', error) + return handleApiError(error) + } + } + + /** + * 메뉴 수정 + * @param {number} menuId - 메뉴 ID + * @param {Object} menuData - 수정할 메뉴 정보 + * @returns {Promise} 수정 결과 + */ + async updateMenu(menuId, menuData) { + try { + console.log('=== 메뉴 수정 API 호출 ===') + console.log('메뉴 ID:', menuId) + console.log('수정 데이터:', menuData) + + if (!menuId || menuId === 'undefined') { + throw new Error('올바른 메뉴 ID가 필요합니다') + } + + const numericMenuId = parseInt(menuId) + if (isNaN(numericMenuId)) { + throw new Error('메뉴 ID는 숫자여야 합니다') + } + + const requestData = { + menuName: menuData.menuName, + category: menuData.category, + price: parseInt(menuData.price) || 0, + description: menuData.description || '' + } + + console.log('백엔드 전송 데이터:', requestData) + + // PUT /api/menu/{menuId} + const response = await menuApi.put(`/${numericMenuId}`, requestData) + + console.log('메뉴 수정 API 응답:', response.data) + + if (response.data && response.data.status === 200) { + return formatSuccessResponse(response.data.data, '메뉴가 성공적으로 수정되었습니다.') + } else { + throw new Error(response.data.message || '메뉴 수정에 실패했습니다.') + } + } catch (error) { + console.error('메뉴 수정 실패:', error) + return handleApiError(error) + } + } + + /** + * 메뉴 삭제 + * @param {number} menuId - 메뉴 ID + * @returns {Promise} 삭제 결과 + */ + async deleteMenu(menuId) { + try { + console.log('=== 메뉴 삭제 API 호출 ===') + console.log('메뉴 ID:', menuId) + + if (!menuId || menuId === 'undefined') { + throw new Error('올바른 메뉴 ID가 필요합니다') + } + + const numericMenuId = parseInt(menuId) + if (isNaN(numericMenuId)) { + throw new Error('메뉴 ID는 숫자여야 합니다') + } + + // DELETE /api/menu/{menuId} + const response = await menuApi.delete(`/${numericMenuId}`) + + console.log('메뉴 삭제 API 응답:', response.data) + + if (response.data && response.data.status === 200) { + return formatSuccessResponse(response.data.data, '메뉴가 성공적으로 삭제되었습니다.') + } else { + throw new Error(response.data.message || '메뉴 삭제에 실패했습니다.') + } + } catch (error) { + console.error('메뉴 삭제 실패:', error) + return handleApiError(error) + } + } +} + +// 싱글톤 인스턴스 생성 및 export +export const menuService = new MenuService() +export default menuService + +// 디버깅을 위한 전역 노출 (개발 환경에서만) +if (process.env.NODE_ENV === 'development') { + window.menuService = menuService +} \ No newline at end of file diff --git a/src/services/store.js b/src/services/store.js index 57916ba..8a0a997 100644 --- a/src/services/store.js +++ b/src/services/store.js @@ -1,9 +1,18 @@ +<<<<<<< HEAD //* src/services/store.js - 매출 API 수정버전 import { storeApi, salesApi, handleApiError, formatSuccessResponse } from './api.js' /** * 매장 관련 API 서비스 * 유저스토리: STR-005, STR-010, STR-015, STR-020, STR-025, STR-030, STR-035, STR-040 +======= +//* src/services/store.js - 매장 서비스 완전 수정 +import { storeApi, handleApiError, formatSuccessResponse } from './api.js' + +/** + * 매장 관련 API 서비스 + * 백엔드 Store Controller와 연동 (포트 8082) +>>>>>>> 87871709f2bbcdab5df004a3954d6ba0af3cadce */ class StoreService { /** @@ -13,12 +22,17 @@ class StoreService { */ async registerStore(storeData) { try { - const response = await storeApi.post('/register', { + console.log('=== 매장 등록 API 호출 ===') + console.log('요청 데이터:', storeData) + + // 백엔드 StoreCreateRequest에 맞는 형태로 변환 + const requestData = { storeName: storeData.storeName, storeImage: storeData.storeImage, businessType: storeData.businessType, address: storeData.address, phoneNumber: storeData.phoneNumber, +<<<<<<< HEAD businessNumber: storeData.businessNumber, instaAccounts: storeData.instaAccounts, blogAccounts: storeData.blogAccounts, @@ -29,7 +43,46 @@ class StoreService { }) return formatSuccessResponse(response.data.data, '매장이 등록되었습니다.') +======= + businessHours: storeData.businessHours, + closedDays: storeData.closedDays, + seatCount: parseInt(storeData.seatCount) || 0, + instaAccounts: storeData.instaAccounts || '', + blogAccounts: storeData.blogAccounts || '', + description: storeData.description || '' + } + + console.log('=== 각 필드 상세 검증 ===') + console.log('storeName:', requestData.storeName, '(타입:', typeof requestData.storeName, ')') + console.log('businessType:', requestData.businessType, '(타입:', typeof requestData.businessType, ')') + console.log('address:', requestData.address, '(타입:', typeof requestData.address, ')') + console.log('seatCount:', requestData.seatCount, '(타입:', typeof requestData.seatCount, ')') + + console.log('백엔드 전송 데이터:', requestData) + + const response = await storeApi.post('/register', requestData) + + console.log('매장 등록 API 응답:', response.data) + + // 백엔드 응답 구조에 맞게 처리 + if (response.data && (response.data.status === 200 || response.data.success !== false)) { + return { + success: true, + message: response.data.message || '매장이 등록되었습니다.', + data: response.data.data + } + } else { + throw new Error(response.data.message || '매장 등록에 실패했습니다.') + } +>>>>>>> 87871709f2bbcdab5df004a3954d6ba0af3cadce } catch (error) { + console.error('매장 등록 실패:', error) + + if (error.response) { + console.error('응답 상태:', error.response.status) + console.error('응답 데이터:', error.response.data) + } + return handleApiError(error) } } @@ -40,37 +93,125 @@ class StoreService { */ async getStore() { try { - const response = await storeApi.get('/') - - return formatSuccessResponse(response.data.data, '매장 정보를 조회했습니다.') + console.log('=== 매장 정보 조회 API 호출 ===') + + // URL 슬래시 문제 해결: 빈 문자열로 호출하여 '/api/store'가 되도록 함 + const response = await storeApi.get('') + + console.log('매장 정보 조회 API 응답:', response.data) + + // 백엔드 응답 구조 수정: 디버깅 결과에 맞게 처리 + if (response.data && response.data.status === 200 && response.data.data) { + console.log('✅ 매장 정보 조회 성공:', response.data.data) + return { + success: true, + message: response.data.message || '매장 정보를 조회했습니다.', + data: response.data.data + } + } else if (response.data && response.data.status === 404) { + // 매장이 없는 경우 + console.log('⚠️ 등록된 매장이 없음') + return { + success: false, + message: '등록된 매장이 없습니다', + data: null + } + } else { + console.warn('예상치 못한 응답 구조:', response.data) + throw new Error(response.data.message || '매장 정보를 찾을 수 없습니다.') + } } catch (error) { + console.error('매장 정보 조회 실패:', error) + + // 404 오류 처리 (매장이 없음) + if (error.response?.status === 404) { + return { + success: false, + message: '등록된 매장이 없습니다', + data: null + } + } + + // 500 오류 처리 (서버 내부 오류) + if (error.response?.status === 500) { + console.error('서버 내부 오류 - 백엔드 로그 확인 필요:', error.response?.data) + return { + success: false, + message: '서버 오류가 발생했습니다. 관리자에게 문의하세요.', + data: null + } + } + return handleApiError(error) } } /** * 매장 정보 수정 (STR-010: 매장 수정) + * @param {number} storeId - 매장 ID (현재는 사용하지 않음 - JWT에서 사용자 확인) * @param {Object} storeData - 수정할 매장 정보 * @returns {Promise} 매장 수정 결과 */ - async updateStore(storeData) { + async updateStore(storeId, storeData) { try { +<<<<<<< HEAD const response = await storeApi.put('/', storeData) return formatSuccessResponse(response.data.data, '매장 정보가 수정되었습니다.') +======= + console.log('=== 매장 정보 수정 API 호출 ===') + console.log('요청 데이터:', storeData) + + // 백엔드 StoreUpdateRequest에 맞는 형태로 변환 + const requestData = { + storeName: storeData.storeName, + businessType: storeData.businessType, + address: storeData.address, + phoneNumber: storeData.phoneNumber, + businessHours: storeData.businessHours, + closedDays: storeData.closedDays, + seatCount: parseInt(storeData.seatCount) || 0, + instaAccounts: storeData.instaAccounts || '', + blogAccounts: storeData.blogAccounts || '', + description: storeData.description || '' + } + + console.log('백엔드 전송 데이터:', requestData) + + // PUT 요청 (storeId는 JWT에서 추출하므로 URL에 포함하지 않음) + const response = await storeApi.put('/', requestData) + + console.log('매장 정보 수정 API 응답:', response.data) + + if (response.data && (response.data.status === 200 || response.data.success !== false)) { + return { + success: true, + message: response.data.message || '매장 정보가 수정되었습니다.', + data: response.data.data + } + } else { + throw new Error(response.data.message || '매장 정보 수정에 실패했습니다.') + } +>>>>>>> 87871709f2bbcdab5df004a3954d6ba0af3cadce } catch (error) { + console.error('매장 정보 수정 실패:', error) return handleApiError(error) } } /** * 매출 정보 조회 (STR-020: 대시보드) +<<<<<<< HEAD * ⚠️ 수정: salesApi 사용하고 storeId 매개변수 추가 * @param {number} storeId - 매장 ID +======= + * @param {string} period - 조회 기간 (today, week, month, year) +>>>>>>> 87871709f2bbcdab5df004a3954d6ba0af3cadce * @returns {Promise} 매출 정보 */ - async getSales(storeId) { + async getSales(period = 'today') { try { +<<<<<<< HEAD // storeId가 없으면 먼저 매장 정보를 조회해서 storeId를 가져옴 if (!storeId) { const storeResponse = await this.getStore() @@ -85,6 +226,18 @@ class StoreService { const response = await salesApi.get(`/${storeId}`) return formatSuccessResponse(response.data.data, '매출 정보를 조회했습니다.') +======= + // 현재는 목업 데이터 반환 (추후 실제 API 연동 시 수정) + const mockSalesData = { + todaySales: 150000, + yesterdaySales: 120000, + changeRate: 25.0, + monthlyTarget: 3000000, + achievementRate: 45.2 + } + + return formatSuccessResponse(mockSalesData, '매출 정보를 조회했습니다.') +>>>>>>> 87871709f2bbcdab5df004a3954d6ba0af3cadce } catch (error) { console.error('매출 정보 조회 실패:', error) return handleApiError(error) @@ -92,6 +245,7 @@ class StoreService { } /** +<<<<<<< HEAD * 메뉴 등록 (STR-030: 메뉴 등록) * ⚠️ 수정: 올바른 API 경로 사용 * @param {Object} menuData - 메뉴 정보 @@ -117,10 +271,14 @@ class StoreService { /** * 메뉴 목록 조회 (STR-025: 메뉴 조회) * @param {number} storeId - 매장 ID +======= + * 메뉴 목록 조회 (개발 예정) +>>>>>>> 87871709f2bbcdab5df004a3954d6ba0af3cadce * @returns {Promise} 메뉴 목록 */ - async getMenus(storeId) { + async getMenus() { try { +<<<<<<< HEAD const response = await storeApi.get(`/menu?storeId=${storeId}`) return formatSuccessResponse(response.data.data, '메뉴 목록을 조회했습니다.') @@ -169,11 +327,42 @@ class StoreService { const response = await storeApi.get('/statistics') return formatSuccessResponse(response.data.data, '매장 통계를 조회했습니다.') +======= + // 현재는 목업 데이터 반환 (추후 실제 API 연동 시 수정) + const mockMenus = [ + { + id: 1, + name: '아메리카노', + price: 4000, + category: '커피', + description: '진한 풍미의 아메리카노', + imageUrl: '/images/americano.jpg', + isAvailable: true + }, + { + id: 2, + name: '카페라떼', + price: 4500, + category: '커피', + description: '부드러운 우유가 들어간 라떼', + imageUrl: '/images/latte.jpg', + isAvailable: true + } + ] + + return formatSuccessResponse(mockMenus, '메뉴 목록을 조회했습니다.') +>>>>>>> 87871709f2bbcdab5df004a3954d6ba0af3cadce } catch (error) { return handleApiError(error) } } } +// 싱글톤 인스턴스 생성 및 export export const storeService = new StoreService() -export default storeService \ No newline at end of file +export default storeService + +// 디버깅을 위한 전역 노출 (개발 환경에서만) +if (process.env.NODE_ENV === 'development') { + window.storeService = storeService +} \ No newline at end of file diff --git a/src/store/index.js b/src/store/index.js index ae9412c..da8b7ff 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1,285 +1,207 @@ -//* src/store/index.js -/** - * Pinia 스토어 설정 - * 전역 상태 관리 - */ +//* src/store/index.js - Store 스토어 수정 (fetchMenus 메서드 추가) import { defineStore } from 'pinia' -import authService from '@/services/auth' -import storeService from '@/services/store' -// 인증 스토어 -export const useAuthStore = defineStore('auth', { - state: () => ({ - user: null, - token: localStorage.getItem('token'), - refreshToken: localStorage.getItem('refreshToken'), - isAuthenticated: false - }), - - getters: { - getUserInfo: (state) => state.user, - isLoggedIn: (state) => state.isAuthenticated && !!state.token - }, - - actions: { - async login(credentials) { - try { - const response = await authService.login(credentials) - this.setAuth(response.data) - return response - } catch (error) { - this.clearAuth() - throw error - } - }, - - async register(userData) { - try { - const response = await authService.register(userData) - return response - } catch (error) { - throw error - } - }, - - async logout() { - try { - if (this.token) { - await authService.logout() - } - } catch (error) { - console.error('로그아웃 오류:', error) - } finally { - this.clearAuth() - } - }, - - async refreshUserInfo() { - try { - const response = await authService.getUserInfo() - this.user = response.data - this.isAuthenticated = true - return response - } catch (error) { - this.clearAuth() - throw error - } - }, - - setAuth(authData) { - this.user = authData.user - this.token = authData.accessToken - this.refreshToken = authData.refreshToken - this.isAuthenticated = true - - localStorage.setItem('token', authData.accessToken) - localStorage.setItem('refreshToken', authData.refreshToken) - }, - - clearAuth() { - this.user = null - this.token = null - this.refreshToken = null - this.isAuthenticated = false - - localStorage.removeItem('token') - localStorage.removeItem('refreshToken') - } - } -}) - -// 앱 전역 스토어 -export const useAppStore = defineStore('app', { - state: () => ({ - loading: false, - snackbar: { - show: false, - message: '', - color: 'success', - timeout: 3000 - }, - notifications: [], - notificationCount: 0 - }), - - actions: { - setLoading(status) { - this.loading = status - }, - - showSnackbar(message, color = 'success', timeout = 3000) { - this.snackbar = { - show: true, - message, - color, - timeout - } - }, - - hideSnackbar() { - this.snackbar.show = false - }, - - addNotification(notification) { - this.notifications.unshift({ - id: Date.now(), - timestamp: new Date(), - ...notification - }) - this.notificationCount = this.notifications.length - }, - - clearNotifications() { - this.notifications = [] - this.notificationCount = 0 - } - } -}) - -// 매장 스토어 export const useStoreStore = defineStore('store', { state: () => ({ storeInfo: null, - loading: false - }), - - getters: { - hasStoreInfo: (state) => !!state.storeInfo - }, - - actions: { - setStoreInfo(storeInfo) { - this.storeInfo = storeInfo - }, - - async fetchStoreInfo() { - try { - this.loading = true - const response = await storeService.getStore() // getStoreInfo가 아닌 getStore - this.storeInfo = response.data - return response - } catch (error) { - throw error - } finally { - this.loading = false - } - }, - - async registerStore(storeData) { - try { - this.loading = true - const response = await storeService.registerStore(storeData) - this.storeInfo = response.data - return response - } catch (error) { - throw error - } finally { - this.loading = false - } - }, - - async updateStore(storeId, storeData) { - try { - this.loading = true - const response = await storeService.updateStore(storeId, storeData) - this.storeInfo = response.data - return response - } catch (error) { - throw error - } finally { - this.loading = false - } - }, - - async createStoreInfo(storeData) { - try { - this.loading = true - const response = await storeService.createStoreInfo(storeData) - this.storeInfo = response.data - return response - } catch (error) { - throw error - } finally { - this.loading = false - } - } - } -}) - -// 메뉴 스토어 -export const useMenuStore = defineStore('menu', { - state: () => ({ - menus: [], + menus: [], // 메뉴 목록 추가 loading: false, - totalCount: 0 + error: null }), getters: { - getMenuById: (state) => (id) => { - return state.menus.find(menu => menu.id === id) - }, - - getMenusByCategory: (state) => (category) => { - return state.menus.filter(menu => menu.category === category) - } + hasStoreInfo: (state) => !!state.storeInfo, + isLoading: (state) => state.loading, + hasMenus: (state) => state.menus && state.menus.length > 0 }, actions: { - async fetchMenus() { + /** + * 매장 정보 조회 + */ + async fetchStoreInfo() { + console.log('=== Store 스토어: 매장 정보 조회 시작 ===') + this.loading = true + this.error = null + try { - this.loading = true - const response = await storeService.getMenus() - this.menus = response.data - this.totalCount = response.data.length - return response - } catch (error) { - throw error - } finally { - this.loading = false - } - }, - - async createMenu(menuData) { - try { - this.loading = true - const response = await storeService.createMenu(menuData) - this.menus.push(response.data) - this.totalCount++ - return response - } catch (error) { - throw error - } finally { - this.loading = false - } - }, - - async updateMenu(menuId, menuData) { - try { - this.loading = true - const response = await storeService.updateMenu(menuId, menuData) - const index = this.menus.findIndex(menu => menu.id === menuId) - if (index !== -1) { - this.menus[index] = response.data + // 스토어 서비스 임포트 + const { storeService } = await import('@/services/store') + + console.log('매장 정보 API 호출') + const result = await storeService.getStore() + + 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) { + // 매장 정보가 있는 경우 + console.log('✅ 매장 정보 설정:', result.data) + this.storeInfo = result.data + return { success: true, data: result.data } + } else { + // 매장이 없거나 조회 실패한 경우 + console.log('⚠️ 매장 정보 없음 또는 조회 실패') + this.storeInfo = null + + if (result.message === '등록된 매장이 없습니다') { + return { success: false, message: '등록된 매장이 없습니다' } + } else { + return { success: false, message: result.message || '매장 정보 조회에 실패했습니다' } + } } - return response } catch (error) { - throw error + console.error('=== Store 스토어: 매장 정보 조회 실패 ===') + console.error('Error:', error) + + this.error = error.message + this.storeInfo = null + + // HTTP 상태 코드별 처리 + if (error.response?.status === 404) { + return { success: false, message: '등록된 매장이 없습니다' } + } + + if (error.response?.status >= 500) { + return { success: false, message: '서버 오류가 발생했습니다. 잠시 후 다시 시도해주세요.' } + } + + if (error.response?.status === 401) { + return { success: false, message: '로그인이 필요합니다' } + } + + return { success: false, message: error.message || '매장 정보 조회에 실패했습니다' } } finally { this.loading = false } }, - - async deleteMenu(menuId) { + + /** + * 메뉴 목록 조회 - 실제 API 연동 (매장 ID 필요) + */ + async fetchMenus() { + console.log('=== Store 스토어: 메뉴 목록 조회 시작 ===') + try { - this.loading = true - await storeService.deleteMenu(menuId) - this.menus = this.menus.filter(menu => menu.id !== menuId) - this.totalCount-- + // 매장 정보에서 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) { + // 메뉴 목록이 있는 경우 + console.log('✅ 메뉴 목록 설정:', result.data) + this.menus = result.data + return { success: true, data: result.data } + } else { + // 메뉴가 없거나 조회 실패한 경우 + console.log('⚠️ 메뉴 목록 없음 또는 조회 실패') + this.menus = [] + + if (result.message === '등록된 메뉴가 없습니다') { + return { success: false, message: '등록된 메뉴가 없습니다', data: [] } + } else { + return { success: false, message: result.message || '메뉴 목록 조회에 실패했습니다', data: [] } + } + } } catch (error) { - throw error + console.error('=== Store 스토어: 메뉴 목록 조회 실패 ===') + console.error('Error:', error) + + this.menus = [] + return { success: false, message: error.message || '메뉴 목록을 불러오는데 실패했습니다', data: [] } + } + }, + + /** + * 매장 등록 + */ + async registerStore(storeData) { + console.log('매장 등록 시작:', storeData) + this.loading = true + this.error = null + + try { + const { storeService } = await import('@/services/store') + + const result = await storeService.registerStore(storeData) + + console.log('매장 등록 결과:', result) + + if (result.success) { + // 등록 성공 후 매장 정보 다시 조회 + await this.fetchStoreInfo() + return result + } else { + this.error = result.message + return result + } + } catch (error) { + console.error('매장 등록 실패:', error) + this.error = error.message + return { success: false, message: error.message || '매장 등록에 실패했습니다' } } finally { this.loading = false } + }, + + /** + * 매장 정보 수정 + */ + async updateStore(storeId, storeData) { + console.log('매장 정보 수정 시작:', { storeId, storeData }) + this.loading = true + this.error = null + + try { + const { storeService } = await import('@/services/store') + + const result = await storeService.updateStore(storeId, storeData) + + console.log('매장 수정 결과:', result) + + if (result.success) { + // 수정 성공 후 매장 정보 다시 조회 + await this.fetchStoreInfo() + return result + } else { + this.error = result.message + return result + } + } catch (error) { + console.error('매장 수정 실패:', error) + this.error = error.message + return { success: false, message: error.message || '매장 수정에 실패했습니다' } + } finally { + this.loading = false + } + }, + + /** + * 매장 정보 초기화 + */ + clearStoreInfo() { + this.storeInfo = null + this.menus = [] + this.error = null + this.loading = false } } }) \ No newline at end of file diff --git a/src/store/store.js b/src/store/store.js index 11b5b8a..9b35889 100644 --- a/src/store/store.js +++ b/src/store/store.js @@ -1,3 +1,4 @@ +<<<<<<< HEAD //* src/services/store.js import { api } from './api' import { formatSuccessResponse, formatErrorResponse, handleApiError } from '@/utils/api-helpers' @@ -56,10 +57,159 @@ class StoreService { } catch (error) { console.error('매장 정보 수정 실패:', error) return handleApiError(error) +======= +// src/store/store.js - StoreService 클래스의 getMenus 메서드 올바른 문법으로 수정 + +/** + * 메뉴 목록 조회 (수정된 버전 - storeId 파라미터 추가) + * @param {number} storeId - 매장 ID (옵션, 없으면 목업 데이터 반환) + * @returns {Promise} 메뉴 목록 + */ +async getMenus(storeId) { + try { + console.log('=== 메뉴 목록 조회 API 호출 ===') + console.log('매장 ID:', storeId) + + // storeId가 없으면 목업 데이터 반환 (개발 중) + if (!storeId) { + console.warn('매장 ID가 없어서 목업 데이터 반환') + const mockMenus = [ + { + menuId: 1, // id 대신 menuId 사용 + id: 1, // 호환성을 위해 + name: '아메리카노', + menuName: '아메리카노', // 백엔드 형식 + price: 4000, + category: '커피', + description: '진한 풍미의 아메리카노', + imageUrl: '/images/americano.jpg', + isAvailable: true, + available: true // 백엔드 형식 + }, + { + menuId: 2, + id: 2, + name: '카페라떼', + menuName: '카페라떼', + price: 4500, + category: '커피', + description: '부드러운 우유가 들어간 라떼', + imageUrl: '/images/latte.jpg', + isAvailable: true, + available: true + } + ] + + return formatSuccessResponse(mockMenus, '목업 메뉴 목록을 조회했습니다.') } + + // 실제 백엔드 API 호출 + try { + // 메뉴 API import + const { menuApi } = await import('./api.js') + + // GET /api/menu?storeId={storeId} + const response = await menuApi.get('', { + params: { storeId } + }) + + console.log('메뉴 목록 조회 API 응답:', response.data) + + if (response.data && response.data.status === 200) { + // 백엔드에서 받은 메뉴 데이터를 프론트엔드 형식으로 변환 + const menus = response.data.data.map(menu => ({ + menuId: menu.menuId, + id: menu.menuId, // 호환성을 위해 + storeId: menu.storeId, + menuName: menu.menuName, + name: menu.menuName, // 호환성을 위해 + category: menu.category, + price: menu.price, + description: menu.description, + available: menu.available !== undefined ? menu.available : true, + isAvailable: menu.available !== undefined ? menu.available : true, // 호환성 + imageUrl: menu.imageUrl || '/images/menu-placeholder.png', + createdAt: menu.createdAt, + updatedAt: menu.updatedAt + })) + + return formatSuccessResponse(menus, '메뉴 목록을 조회했습니다.') + } else { + throw new Error(response.data.message || '메뉴 목록을 찾을 수 없습니다.') + } + } catch (apiError) { + console.error('백엔드 API 호출 실패:', apiError) + + // 백엔드 미구현이나 네트워크 오류 시 목업 데이터 반환 + if (apiError.response?.status === 404 || + apiError.code === 'ECONNREFUSED' || + apiError.message.includes('Network Error')) { + console.warn('백엔드 미구현 - 목업 데이터 반환') + + const mockMenus = [ + { + menuId: 1, + id: 1, + storeId: storeId, + name: '아메리카노', + menuName: '아메리카노', + price: 4000, + category: '커피', + description: '진한 풍미의 아메리카노', + imageUrl: '/images/americano.jpg', + isAvailable: true, + available: true + }, + { + menuId: 2, + id: 2, + storeId: storeId, + name: '카페라떼', + menuName: '카페라떼', + price: 4500, + category: '커피', + description: '부드러운 우유가 들어간 라떼', + imageUrl: '/images/latte.jpg', + isAvailable: true, + available: true + } + ] + + return formatSuccessResponse(mockMenus, '목업 메뉴 목록을 조회했습니다. (백엔드 미구현)') + } + + throw apiError +>>>>>>> 87871709f2bbcdab5df004a3954d6ba0af3cadce + } + } catch (error) { + console.error('메뉴 목록 조회 실패:', error) + return handleApiError(error) + } +} + +// 만약 fetchMenus 메서드가 따로 필요하다면 다음과 같이 추가: +/** + * 메뉴 목록 조회 (fetchMenus 별칭) + * @param {number} storeId - 매장 ID + * @returns {Promise} 메뉴 목록 + */ +async fetchMenus(storeId) { + return await this.getMenus(storeId) +} + +// StoreService 클래스 전체 구조 예시: +class StoreService { + // ... 기존 메서드들 (registerStore, getStore, updateStore 등) ... + + /** + * 메뉴 목록 조회 (위의 getMenus 메서드) + */ + async getMenus(storeId) { + // 위의 구현 내용 } /** +<<<<<<< HEAD * 매출 정보 조회 (STR-020: 대시보드) * @param {number} storeId - 매장 ID (기본값: 1) * @returns {Promise} 매출 정보 @@ -274,4 +424,20 @@ export default storeService // 디버깅을 위한 전역 노출 (개발 환경에서만) if (process.env.NODE_ENV === 'development') { window.storeService = storeService -} \ No newline at end of file +} +======= + * 메뉴 목록 조회 별칭 + */ + async fetchMenus(storeId) { + return await this.getMenus(storeId) + } +} + +// 올바른 JavaScript 클래스 메서드 문법: +// ❌ 잘못된 문법: +// async function getMenus(storeId) { ... } +// function async getMenus(storeId) { ... } + +// ✅ 올바른 문법: +// async getMenus(storeId) { ... } +>>>>>>> 87871709f2bbcdab5df004a3954d6ba0af3cadce diff --git a/src/views/StoreManagementView.vue b/src/views/StoreManagementView.vue index 7494f3f..ee2ca70 100644 --- a/src/views/StoreManagementView.vue +++ b/src/views/StoreManagementView.vue @@ -1,4 +1,5 @@ -//* src/views/StoreManagementView.vue +//* src/views/StoreManagementView.vue - 완전한 매장 등록 화면 + \ No newline at end of file