Skip to main content
Glama
yakir-Yang

AI Customer Service MCP Server

by yakir-Yang
data-manager.js10.4 kB
import XLSX from 'xlsx'; import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); export class DataManager { constructor() { this.stores = []; this.orders = []; } async loadData() { try { // 加载门店数据 await this.loadStoresData(); // 加载订单数据 await this.loadOrdersData(); console.error(`数据加载完成: ${this.stores.length} 个门店, ${this.orders.length} 个订单`); } catch (error) { console.error('数据加载失败:', error); throw error; } } async loadStoresData() { const storesPath = path.join(__dirname, '../data/stores.xlsx'); if (!fs.existsSync(storesPath)) { console.error('门店数据文件不存在,使用示例数据'); this.stores = this.getSampleStoresData(); return; } try { // 尝试作为Excel文件读取 const workbook = XLSX.readFile(storesPath); const sheetName = workbook.SheetNames[0]; const worksheet = workbook.Sheets[sheetName]; const data = XLSX.utils.sheet_to_json(worksheet); this.stores = data.map(store => ({ name: store['网点名称'], status: store['状态'], coordinates: store['经纬度'], province: store['省份'], city: store['城市'], district: store['区/县'], address: store['详细地址'], longitude: parseFloat(store['经度']) || 0, latitude: parseFloat(store['纬度']) || 0, type: store['门店类型'], openDate: store['开业时间'], businessHours: store['营业时间'], rating: parseFloat(store['评分']) || 0, phone: store['联系电话'] })); console.error(`从Excel文件加载门店数据: ${this.stores.length} 个门店`); } catch (error) { console.error('Excel文件读取失败,使用示例数据:', error.message); this.stores = this.getSampleStoresData(); } } async loadOrdersData() { const ordersPath = path.join(__dirname, '../data/orders.xlsx'); if (!fs.existsSync(ordersPath)) { console.error('订单数据文件不存在,使用示例数据'); this.orders = this.getSampleOrdersData(); return; } try { // 尝试作为Excel文件读取 const workbook = XLSX.readFile(ordersPath); const sheetName = workbook.SheetNames[0]; const worksheet = workbook.Sheets[sheetName]; const data = XLSX.utils.sheet_to_json(worksheet); this.orders = data.map(order => { // 调试:显示第一条记录的详细信息 if (data.indexOf(order) === 0) { console.error('Excel订单数据字段:', Object.keys(order)); console.error('第一条订单原始数据:', order); } const mappedOrder = { orderId: order['订单号'], userId: order['用户id'], phone: String(order['手机号']), // 确保手机号是字符串类型 deviceId: order['设备id'], location: order['ai6'] || order['租借位置'] || order['租借门店'] || order['门店名称'] || '未知位置', startTime: order['租借开始时间'], returnTime: order['退还时间'], duration: parseInt(order['持续时间/分钟']) || 0, returnStore: order['归还网点'], cost: parseFloat(order['计费']) || 0, status: order['租借状态'], paymentMethod: order['支付方式'] }; // 调试:显示第一条记录的映射结果 if (data.indexOf(order) === 0) { console.error('第一条订单映射结果:', mappedOrder); } return mappedOrder; }); console.error(`从Excel文件加载订单数据: ${this.orders.length} 个订单`); } catch (error) { console.error('Excel文件读取失败,使用示例数据:', error.message); this.orders = this.getSampleOrdersData(); } } getSampleStoresData() { return [ { name: '北京后海-110分店', status: '正常', coordinates: '116.370503,39.946613', province: '北京市', city: '北京市', district: '西城区', address: '北京市西城区羊房胡同甲23号', longitude: 116.3705, latitude: 39.94661, type: '直营店', openDate: '2024/3/1', businessHours: '9:00-22:00', rating: 4.5, phone: '15858905939' }, { name: '北京后海-120分店', status: '正常', coordinates: '116.378818,39.944656', province: '北京市', city: '北京市', district: '西城区', address: '北京市西城区羊房胡同甲35号', longitude: 116.3788, latitude: 39.94466, type: '直营店', openDate: '2018/12/23', businessHours: '9:00-22:00', rating: 4.8, phone: '15890785113' }, { name: '北京后海-125分店', status: '正常', coordinates: '116.374374,39.942185', province: '北京市', city: '北京市', district: '西城区', address: '北京市西城区羊房胡同甲31号', longitude: 116.3744, latitude: 39.94219, type: '直营店', openDate: '2016/8/31', businessHours: '9:00-22:00', rating: 4.7, phone: '15828386811' }, { name: '北京后海-142分店', status: '正常', coordinates: '116.38649,39.943933', province: '北京市', city: '北京市', district: '西城区', address: '北京市西城区后海北沿50号', longitude: 116.3865, latitude: 39.94393, type: '直营店', openDate: '2019/8/4', businessHours: '9:00-22:00', rating: 4.9, phone: '15892847917' }, { name: '北京后海-148分店', status: '正常', coordinates: '116.369155,39.940742', province: '北京市', city: '北京市', district: '西城区', address: '北京市西城区后海北沿70号', longitude: 116.3692, latitude: 39.94074, type: '直营店', openDate: '2020/8/21', businessHours: '9:00-22:00', rating: 4.8, phone: '15817929741' }, { name: '北京后海-154分店', status: '正常', coordinates: '116.377185,39.943402', province: '北京市', city: '北京市', district: '西城区', address: '北京市西城区德内大街羊房胡同9号', longitude: 116.3772, latitude: 39.9434, type: '直营店', openDate: '2018/5/14', businessHours: '9:00-22:00', rating: 3.9, phone: '15813203456' } ]; } getSampleOrdersData() { return [ { orderId: 'PO202508281731220218', userId: 'U10058', phone: '17798762697', deviceId: 'D20219', location: '北京后海-166分店', startTime: '2025-08-28 20:35:00', returnTime: '', duration: 32, returnStore: '', cost: 3.2, status: '已暂停', paymentMethod: 'wechat_pay' }, { orderId: 'PO202508281731220253', userId: 'U10007', phone: '17796499753', deviceId: 'D20254', location: '深圳华强北店', startTime: '2025-08-28 23:20:00', returnTime: '', duration: 239, returnStore: '', cost: 23.9, status: '已暂停', paymentMethod: 'apple_pay' }, { orderId: 'PO202508281731220067', userId: 'U10080', phone: '17796025359', deviceId: 'D20068', location: '北京三里屯太古里-34分店', startTime: '2025-08-28 15:28:00', returnTime: '', duration: 7, returnStore: '', cost: 0.7, status: '进行中', paymentMethod: 'wechat_pay' }, { orderId: 'PO202508281731220282', userId: 'U10045', phone: '17794866582', deviceId: 'D20283', location: '上海外滩-150分店', startTime: '2025-08-28 21:16:00', returnTime: '', duration: 128, returnStore: '', cost: 12.8, status: '已暂停', paymentMethod: 'alipay' }, { orderId: 'PO202508281731220099', userId: 'U10031', phone: '17792350608', deviceId: 'D20100', location: '上海外滩-171分店', startTime: '2025-08-28 22:14:00', returnTime: '', duration: 157, returnStore: '', cost: 15.7, status: '已暂停', paymentMethod: 'alipay' } ]; } // 根据手机号查询订单 queryOrdersByPhone(phone) { console.error(`查询手机号: ${phone}`); console.error(`总订单数: ${this.orders.length}`); console.error(`前5个订单的手机号:`, this.orders.slice(0, 5).map(o => o.phone)); const result = this.orders.filter(order => order.phone === phone); console.error(`找到订单数: ${result.length}`); return result; } // 根据经纬度查询附近门店 queryStoresByLocation(latitude, longitude, limit = 10) { const storesWithDistance = this.stores.map(store => { const distance = this.calculateDistance(latitude, longitude, store.latitude, store.longitude); return { ...store, distance }; }); return storesWithDistance .sort((a, b) => a.distance - b.distance) .slice(0, limit); } // 计算两点间距离(使用Haversine公式) calculateDistance(lat1, lon1, lat2, lon2) { const R = 6371; // 地球半径(公里) const dLat = this.toRadians(lat2 - lat1); const dLon = this.toRadians(lon2 - lon1); const a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(this.toRadians(lat1)) * Math.cos(this.toRadians(lat2)) * Math.sin(dLon/2) * Math.sin(dLon/2); const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); return R * c; } toRadians(degrees) { return degrees * (Math.PI/180); } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/yakir-Yang/mcp_demo'

If you have feedback or need assistance with the MCP directory API, please join our Discord server