api.js store.js edit
This commit is contained in:
parent
43595d5e72
commit
9cbb1b63cc
@ -14,14 +14,7 @@ const getApiUrls = () => {
|
|||||||
// ⚠️ 수정: 매출 API는 store 서비스 (포트 8082)
|
// ⚠️ 수정: 매출 API는 store 서비스 (포트 8082)
|
||||||
SALES_URL: config.SALES_URL || 'http://localhost:8082/api/sales',
|
SALES_URL: config.SALES_URL || 'http://localhost:8082/api/sales',
|
||||||
// ⚠️ 수정: 추천 API는 ai-recommend 서비스 (포트 8084)
|
// ⚠️ 수정: 추천 API는 ai-recommend 서비스 (포트 8084)
|
||||||
RECOMMEND_URL: config.RECOMMEND_URL || 'http://localhost:8084/api/recommendations',
|
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',
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,54 +1,164 @@
|
|||||||
<<<<<<< HEAD
|
//* src/services/store.js - 병합 충돌 해결된 매장 서비스
|
||||||
//* src/services/store.js
|
import { storeApi, menuApi, handleApiError, formatSuccessResponse } from './api.js'
|
||||||
import { api } from './api'
|
|
||||||
import { formatSuccessResponse, formatErrorResponse, handleApiError } from '@/utils/api-helpers'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 매장 관련 API 서비스
|
* 매장 관련 API 서비스
|
||||||
* 매장 정보 조회, 수정, 매출 정보 등을 처리합니다.
|
* 백엔드 Store Controller와 연동 (포트 8082)
|
||||||
*/
|
*/
|
||||||
class StoreService {
|
class StoreService {
|
||||||
constructor() {
|
/**
|
||||||
this.baseURL = '/api/store'
|
* 매장 등록 (STR-015: 매장 등록)
|
||||||
this.salesURL = '/api/sales'
|
* @param {Object} storeData - 매장 정보
|
||||||
|
* @returns {Promise<Object>} 매장 등록 결과
|
||||||
|
*/
|
||||||
|
async registerStore(storeData) {
|
||||||
|
try {
|
||||||
|
console.log('=== 매장 등록 API 호출 ===')
|
||||||
|
console.log('요청 데이터:', storeData)
|
||||||
|
|
||||||
|
// 백엔드 StoreCreateRequest에 맞는 형태로 변환
|
||||||
|
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('=== 각 필드 상세 검증 ===')
|
||||||
* 매장 정보 조회 (STR-001: 매장 정보 조회)
|
console.log('storeName:', requestData.storeName, '(타입:', typeof requestData.storeName, ')')
|
||||||
* @returns {Promise<Object>} 매장 정보
|
console.log('businessType:', requestData.businessType, '(타입:', typeof requestData.businessType, ')')
|
||||||
*/
|
console.log('address:', requestData.address, '(타입:', typeof requestData.address, ')')
|
||||||
async getStore() {
|
console.log('seatCount:', requestData.seatCount, '(타입:', typeof requestData.seatCount, ')')
|
||||||
try {
|
|
||||||
const response = await api.get(`${this.baseURL}/info`)
|
|
||||||
|
|
||||||
if (response.data.success) {
|
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 {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
message: '매장 정보를 조회했습니다.',
|
message: response.data.message || '매장이 등록되었습니다.',
|
||||||
data: response.data.data
|
data: response.data.data
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error(response.data.message || '매장 정보 조회에 실패했습니다.')
|
throw new Error(response.data.message || '매장 등록에 실패했습니다.')
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('매장 정보 조회 실패:', error)
|
console.error('매장 등록 실패:', error)
|
||||||
|
|
||||||
|
if (error.response) {
|
||||||
|
console.error('응답 상태:', error.response.status)
|
||||||
|
console.error('응답 데이터:', error.response.data)
|
||||||
|
}
|
||||||
|
|
||||||
return handleApiError(error)
|
return handleApiError(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 매장 정보 수정 (STR-002: 매장 정보 수정)
|
* 매장 정보 조회 (STR-005: 매장 정보 관리)
|
||||||
* @param {Object} storeData - 수정할 매장 정보
|
* @returns {Promise<Object>} 매장 정보
|
||||||
* @returns {Promise<Object>} 수정된 매장 정보
|
|
||||||
*/
|
*/
|
||||||
async updateStore(storeData) {
|
async getStore() {
|
||||||
try {
|
try {
|
||||||
const response = await api.put(`${this.baseURL}/info`, storeData)
|
console.log('=== 매장 정보 조회 API 호출 ===')
|
||||||
|
|
||||||
if (response.data.success) {
|
// 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 {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
message: '매장 정보를 수정했습니다.',
|
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<Object>} 매장 수정 결과
|
||||||
|
*/
|
||||||
|
async updateStore(storeId, storeData) {
|
||||||
|
try {
|
||||||
|
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
|
data: response.data.data
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -57,11 +167,44 @@ class StoreService {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('매장 정보 수정 실패:', error)
|
console.error('매장 정보 수정 실패:', error)
|
||||||
return handleApiError(error)
|
return handleApiError(error)
|
||||||
=======
|
}
|
||||||
// src/store/store.js - StoreService 클래스의 getMenus 메서드 올바른 문법으로 수정
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 메뉴 목록 조회 (수정된 버전 - storeId 파라미터 추가)
|
* 매출 정보 조회 (STR-020: 대시보드)
|
||||||
|
* @param {string} period - 조회 기간 (today, week, month, year)
|
||||||
|
* @returns {Promise<Object>} 매출 정보
|
||||||
|
*/
|
||||||
|
async getSales(period = 'today') {
|
||||||
|
try {
|
||||||
|
console.log('=== 매출 정보 조회 API 호출 ===')
|
||||||
|
console.log('조회 기간:', period)
|
||||||
|
|
||||||
|
// 현재는 목업 데이터 반환 (추후 실제 API 연동 시 수정)
|
||||||
|
const mockSalesData = {
|
||||||
|
todaySales: 150000,
|
||||||
|
yesterdaySales: 120000,
|
||||||
|
changeRate: 25.0,
|
||||||
|
monthlyTarget: 3000000,
|
||||||
|
achievementRate: 45.2,
|
||||||
|
yearSales: this.generateMockYearSales()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 매출 트렌드 분석 추가
|
||||||
|
if (mockSalesData.yearSales && mockSalesData.yearSales.length > 0) {
|
||||||
|
mockSalesData.trendAnalysis = this.analyzeSalesTrend(mockSalesData.yearSales)
|
||||||
|
mockSalesData.chartData = this.prepareChartData(mockSalesData.yearSales)
|
||||||
|
}
|
||||||
|
|
||||||
|
return formatSuccessResponse(mockSalesData, '매출 정보를 조회했습니다.')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('매출 정보 조회 실패:', error)
|
||||||
|
return handleApiError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 메뉴 목록 조회 (최종 통합 버전 - 모든 충돌 해결)
|
||||||
* @param {number} storeId - 매장 ID (옵션, 없으면 목업 데이터 반환)
|
* @param {number} storeId - 매장 ID (옵션, 없으면 목업 데이터 반환)
|
||||||
* @returns {Promise<Object>} 메뉴 목록
|
* @returns {Promise<Object>} 메뉴 목록
|
||||||
*/
|
*/
|
||||||
@ -99,15 +242,11 @@ async getMenus(storeId) {
|
|||||||
available: true
|
available: true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
return formatSuccessResponse(mockMenus, '목업 메뉴 목록을 조회했습니다.')
|
return formatSuccessResponse(mockMenus, '목업 메뉴 목록을 조회했습니다.')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 실제 백엔드 API 호출
|
// 실제 백엔드 API 호출 시도
|
||||||
try {
|
try {
|
||||||
// 메뉴 API import
|
|
||||||
const { menuApi } = await import('./api.js')
|
|
||||||
|
|
||||||
// GET /api/menu?storeId={storeId}
|
// GET /api/menu?storeId={storeId}
|
||||||
const response = await menuApi.get('', {
|
const response = await menuApi.get('', {
|
||||||
params: { storeId }
|
params: { storeId }
|
||||||
@ -179,7 +318,6 @@ async getMenus(storeId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
throw apiError
|
throw apiError
|
||||||
>>>>>>> 87871709f2bbcdab5df004a3954d6ba0af3cadce
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('메뉴 목록 조회 실패:', error)
|
console.error('메뉴 목록 조회 실패:', error)
|
||||||
@ -187,9 +325,8 @@ async getMenus(storeId) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 만약 fetchMenus 메서드가 따로 필요하다면 다음과 같이 추가:
|
|
||||||
/**
|
/**
|
||||||
* 메뉴 목록 조회 (fetchMenus 별칭)
|
* 메뉴 목록 조회 별칭 (fetchMenus)
|
||||||
* @param {number} storeId - 매장 ID
|
* @param {number} storeId - 매장 ID
|
||||||
* @returns {Promise<Object>} 메뉴 목록
|
* @returns {Promise<Object>} 메뉴 목록
|
||||||
*/
|
*/
|
||||||
@ -197,81 +334,85 @@ async fetchMenus(storeId) {
|
|||||||
return await this.getMenus(storeId)
|
return await this.getMenus(storeId)
|
||||||
}
|
}
|
||||||
|
|
||||||
// StoreService 클래스 전체 구조 예시:
|
|
||||||
class StoreService {
|
|
||||||
// ... 기존 메서드들 (registerStore, getStore, updateStore 등) ...
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 메뉴 목록 조회 (위의 getMenus 메서드)
|
* 목업 메뉴 데이터 생성
|
||||||
|
* @param {number} storeId - 매장 ID (옵션)
|
||||||
|
* @returns {Array} 목업 메뉴 배열
|
||||||
*/
|
*/
|
||||||
async getMenus(storeId) {
|
getMockMenus(storeId = null) {
|
||||||
// 위의 구현 내용
|
return [
|
||||||
}
|
{
|
||||||
|
menuId: 1,
|
||||||
/**
|
id: 1,
|
||||||
<<<<<<< HEAD
|
storeId: storeId,
|
||||||
* 매출 정보 조회 (STR-020: 대시보드)
|
name: '아메리카노',
|
||||||
* @param {number} storeId - 매장 ID (기본값: 1)
|
menuName: '아메리카노',
|
||||||
* @returns {Promise<Object>} 매출 정보
|
price: 4000,
|
||||||
*/
|
category: '커피',
|
||||||
async getSales(storeId = 1) {
|
description: '진한 풍미의 아메리카노',
|
||||||
try {
|
imageUrl: '/images/americano.jpg',
|
||||||
console.log('매출 조회 API 호출:', `${this.salesURL}/${storeId}`)
|
isAvailable: true,
|
||||||
|
available: true
|
||||||
const response = await api.get(`${this.salesURL}/${storeId}`)
|
|
||||||
|
|
||||||
if (response.data.success) {
|
|
||||||
const salesData = response.data.data
|
|
||||||
|
|
||||||
// API 응답 데이터 로그
|
|
||||||
console.log('매출 API 응답:', salesData)
|
|
||||||
|
|
||||||
// yearSales 데이터가 있는지 확인하고 변곡점 계산
|
|
||||||
let processedData = {
|
|
||||||
todaySales: salesData.todaySales || 0,
|
|
||||||
monthSales: salesData.monthSales || 0,
|
|
||||||
previousDayComparison: salesData.previousDayComparison || 0,
|
|
||||||
yearSales: salesData.yearSales || []
|
|
||||||
}
|
|
||||||
|
|
||||||
// 변곡점 분석 추가
|
|
||||||
if (salesData.yearSales && salesData.yearSales.length > 0) {
|
|
||||||
processedData.trendAnalysis = this.analyzeSalesTrend(salesData.yearSales)
|
|
||||||
processedData.chartData = this.prepareChartData(salesData.yearSales)
|
|
||||||
}
|
|
||||||
|
|
||||||
return formatSuccessResponse(processedData, '매출 정보를 조회했습니다.')
|
|
||||||
} else {
|
|
||||||
throw new Error(response.data.message || '매출 정보 조회에 실패했습니다.')
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('매출 정보 조회 실패:', error)
|
|
||||||
|
|
||||||
// API 오류 시 기본 데이터 반환 (개발 단계)
|
|
||||||
const fallbackData = {
|
|
||||||
todaySales: 170000,
|
|
||||||
monthSales: 4500000,
|
|
||||||
previousDayComparison: 15000,
|
|
||||||
yearSales: [],
|
|
||||||
trendAnalysis: {
|
|
||||||
inflectionPoints: [],
|
|
||||||
overallTrend: 'stable',
|
|
||||||
growthRate: 0
|
|
||||||
},
|
},
|
||||||
chartData: {
|
{
|
||||||
labels: [],
|
menuId: 2,
|
||||||
salesData: [],
|
id: 2,
|
||||||
targetData: []
|
storeId: storeId,
|
||||||
|
name: '카페라떼',
|
||||||
|
menuName: '카페라떼',
|
||||||
|
price: 4500,
|
||||||
|
category: '커피',
|
||||||
|
description: '부드러운 우유가 들어간 라떼',
|
||||||
|
imageUrl: '/images/latte.jpg',
|
||||||
|
isAvailable: true,
|
||||||
|
available: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
menuId: 3,
|
||||||
|
id: 3,
|
||||||
|
storeId: storeId,
|
||||||
|
name: '에스프레소',
|
||||||
|
menuName: '에스프레소',
|
||||||
|
price: 3500,
|
||||||
|
category: '커피',
|
||||||
|
description: '진한 에스프레소 한 잔',
|
||||||
|
imageUrl: '/images/espresso.jpg',
|
||||||
|
isAvailable: true,
|
||||||
|
available: true
|
||||||
}
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
return formatSuccessResponse(fallbackData, '임시 매출 데이터를 표시합니다.')
|
/**
|
||||||
|
* 목업 연간 매출 데이터 생성
|
||||||
|
* @returns {Array} 목업 매출 데이터
|
||||||
|
*/
|
||||||
|
generateMockYearSales() {
|
||||||
|
const salesData = []
|
||||||
|
const today = new Date()
|
||||||
|
|
||||||
|
// 최근 90일 데이터 생성
|
||||||
|
for (let i = 89; i >= 0; i--) {
|
||||||
|
const date = new Date(today)
|
||||||
|
date.setDate(date.getDate() - i)
|
||||||
|
|
||||||
|
// 랜덤하지만 현실적인 매출 패턴 생성
|
||||||
|
const baseAmount = 120000 + Math.random() * 80000 // 120,000 ~ 200,000
|
||||||
|
const weekendBonus = date.getDay() === 0 || date.getDay() === 6 ? 1.3 : 1.0
|
||||||
|
const monthlyTrend = 1 + (Math.sin(i / 30) * 0.2) // 월별 트렌드
|
||||||
|
|
||||||
|
salesData.push({
|
||||||
|
salesDate: date.toISOString().split('T')[0],
|
||||||
|
salesAmount: Math.round(baseAmount * weekendBonus * monthlyTrend)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return salesData
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 매출 트렌드 분석 및 변곡점 계산
|
* 매출 트렌드 분석 및 변곡점 계산
|
||||||
* @param {Array} yearSales - 연간 매출 데이터 (Sales 엔티티 배열)
|
* @param {Array} yearSales - 연간 매출 데이터
|
||||||
* @returns {Object} 트렌드 분석 결과
|
* @returns {Object} 트렌드 분석 결과
|
||||||
*/
|
*/
|
||||||
analyzeSalesTrend(yearSales) {
|
analyzeSalesTrend(yearSales) {
|
||||||
@ -283,7 +424,7 @@ class StoreService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 날짜순 정렬 (salesDate 기준)
|
// 날짜순 정렬
|
||||||
const sortedData = [...yearSales].sort((a, b) =>
|
const sortedData = [...yearSales].sort((a, b) =>
|
||||||
new Date(a.salesDate) - new Date(b.salesDate)
|
new Date(a.salesDate) - new Date(b.salesDate)
|
||||||
)
|
)
|
||||||
@ -300,11 +441,10 @@ class StoreService {
|
|||||||
const currentWeekAvg = this.calculateMovingAverage(dailyData, i, 7)
|
const currentWeekAvg = this.calculateMovingAverage(dailyData, i, 7)
|
||||||
const nextWeekAvg = this.calculateMovingAverage(dailyData, i + 7, 7)
|
const nextWeekAvg = this.calculateMovingAverage(dailyData, i + 7, 7)
|
||||||
|
|
||||||
// 변곡점 조건: 이전 주 → 현재 주 → 다음 주의 트렌드 변화
|
|
||||||
const trend1 = currentWeekAvg - prevWeekAvg
|
const trend1 = currentWeekAvg - prevWeekAvg
|
||||||
const trend2 = nextWeekAvg - currentWeekAvg
|
const trend2 = nextWeekAvg - currentWeekAvg
|
||||||
|
|
||||||
// 트렌드 방향이 바뀌고 변화량이 일정 이상인 경우
|
// 변곡점 조건: 트렌드 방향 변화 + 임계값 초과
|
||||||
if (Math.sign(trend1) !== Math.sign(trend2) &&
|
if (Math.sign(trend1) !== Math.sign(trend2) &&
|
||||||
Math.abs(trend1) > 10000 && Math.abs(trend2) > 10000) {
|
Math.abs(trend1) > 10000 && Math.abs(trend2) > 10000) {
|
||||||
|
|
||||||
@ -317,7 +457,7 @@ class StoreService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 전체적인 트렌드 계산
|
// 전체 트렌드 계산
|
||||||
const firstWeekAvg = this.calculateMovingAverage(dailyData, 0, 7)
|
const firstWeekAvg = this.calculateMovingAverage(dailyData, 0, 7)
|
||||||
const lastWeekAvg = this.calculateMovingAverage(dailyData, dailyData.length - 7, 7)
|
const lastWeekAvg = this.calculateMovingAverage(dailyData, dailyData.length - 7, 7)
|
||||||
const growthRate = ((lastWeekAvg - firstWeekAvg) / firstWeekAvg) * 100
|
const growthRate = ((lastWeekAvg - firstWeekAvg) / firstWeekAvg) * 100
|
||||||
@ -359,10 +499,10 @@ class StoreService {
|
|||||||
return { labels: [], salesData: [], targetData: [] }
|
return { labels: [], salesData: [], targetData: [] }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 최근 90일 데이터만 사용 (차트 표시용)
|
// 최근 30일 데이터만 사용 (차트 표시용)
|
||||||
const sortedData = [...yearSales]
|
const sortedData = [...yearSales]
|
||||||
.sort((a, b) => new Date(a.salesDate) - new Date(b.salesDate))
|
.sort((a, b) => new Date(a.salesDate) - new Date(b.salesDate))
|
||||||
.slice(-90)
|
.slice(-30)
|
||||||
|
|
||||||
const labels = sortedData.map(item => {
|
const labels = sortedData.map(item => {
|
||||||
const date = new Date(item.salesDate)
|
const date = new Date(item.salesDate)
|
||||||
@ -373,7 +513,7 @@ class StoreService {
|
|||||||
|
|
||||||
// 목표 매출 라인 (평균의 110%)
|
// 목표 매출 라인 (평균의 110%)
|
||||||
const averageSales = salesData.reduce((a, b) => a + b, 0) / salesData.length
|
const averageSales = salesData.reduce((a, b) => a + b, 0) / salesData.length
|
||||||
const targetData = salesData.map(() => averageSales * 1.1)
|
const targetData = salesData.map(() => Math.round(averageSales * 1.1))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
labels,
|
labels,
|
||||||
@ -381,40 +521,6 @@ class StoreService {
|
|||||||
targetData
|
targetData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 메뉴 목록 조회 (개발 예정)
|
|
||||||
* @returns {Promise<Object>} 메뉴 목록
|
|
||||||
*/
|
|
||||||
async getMenus() {
|
|
||||||
try {
|
|
||||||
// 현재는 목업 데이터 반환 (추후 실제 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, '메뉴 목록을 조회했습니다.')
|
|
||||||
} catch (error) {
|
|
||||||
return handleApiError(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 싱글톤 인스턴스 생성 및 export
|
// 싱글톤 인스턴스 생성 및 export
|
||||||
@ -425,19 +531,3 @@ export default storeService
|
|||||||
if (process.env.NODE_ENV === 'development') {
|
if (process.env.NODE_ENV === 'development') {
|
||||||
window.storeService = storeService
|
window.storeService = storeService
|
||||||
}
|
}
|
||||||
=======
|
|
||||||
* 메뉴 목록 조회 별칭
|
|
||||||
*/
|
|
||||||
async fetchMenus(storeId) {
|
|
||||||
return await this.getMenus(storeId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 올바른 JavaScript 클래스 메서드 문법:
|
|
||||||
// ❌ 잘못된 문법:
|
|
||||||
// async function getMenus(storeId) { ... }
|
|
||||||
// function async getMenus(storeId) { ... }
|
|
||||||
|
|
||||||
// ✅ 올바른 문법:
|
|
||||||
// async getMenus(storeId) { ... }
|
|
||||||
>>>>>>> 87871709f2bbcdab5df004a3954d6ba0af3cadce
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user