const Price = require('../models/Price'); const { validateAndCleanPricesData } = require('../utils/validator'); /** * 价格服务 * 处理价格相关的业务逻辑 */ class PriceService { /** * 按地区查询价格 */ static async getByRegion(region, date, page = 1, pageSize = 20) { try { const prices = await Price.getByRegion(region, date); // 手动分页 const total = prices.length; const startIndex = (page - 1) * pageSize; const endIndex = startIndex + parseInt(pageSize, 10); const paginatedData = prices.slice(startIndex, endIndex); return { success: true, data: paginatedData, pagination: { page: parseInt(page, 10), pageSize: parseInt(pageSize, 10), total, totalPages: Math.ceil(total / pageSize) } }; } catch (error) { throw error; } } /** * 搜索价格数据 */ static async search(filters) { try { // 解析分页参数 const page = parseInt(filters.page) || 1; const pageSize = parseInt(filters.pageSize) || 20; const searchFilters = { material: filters.material, specification: filters.specification, startDate: filters.startDate, endDate: filters.endDate, region: filters.region, page, pageSize }; // 获取数据 const data = await Price.search(searchFilters); // 获取总数 const total = await Price.count({ material: filters.material, specification: filters.specification, startDate: filters.startDate, endDate: filters.endDate, region: filters.region }); return { success: true, data, pagination: { page, pageSize, total, totalPages: Math.ceil(total / pageSize) } }; } catch (error) { throw error; } } /** * 获取价格统计 */ static async getStats(filters) { try { const stats = await Price.getStats(filters); // 计算趋势(如果提供了天数) let trend = null; let changeRate = null; if (filters.days && stats.avgPrice) { const previousStats = await Price.getStats({ region: filters.region, material: filters.material, days: parseInt(filters.days) * 2 // 获取双倍天数的范围 }); if (previousStats.avgPrice) { const currentAvg = parseFloat(stats.avgPrice); const previousAvg = parseFloat(previousStats.avgPrice); const change = currentAvg - previousAvg; changeRate = (change / previousAvg * 100).toFixed(2); trend = change > 0 ? 'up' : change < 0 ? 'down' : 'stable'; } } return { success: true, data: { count: stats.count, avgPrice: stats.avgPrice ? parseFloat(parseFloat(stats.avgPrice).toFixed(2)) : null, minPrice: stats.minPrice, maxPrice: stats.maxPrice, stdDev: stats.stdDev ? parseFloat(parseFloat(stats.stdDev).toFixed(2)) : null, trend, changeRate: changeRate ? `${changeRate > 0 ? '+' : ''}${changeRate}%` : null } }; } catch (error) { throw error; } } /** * 获取价格趋势 */ static async getTrend(filters) { try { const trend = await Price.getTrend(filters); return { success: true, data: trend.map(item => ({ date: item.date, avgPrice: item.avgPrice ? parseFloat(parseFloat(item.avgPrice).toFixed(2)) : null, minPrice: item.minPrice, maxPrice: item.maxPrice })), meta: { total: trend.length, filters: { region: filters.region || null, material: filters.material || null, days: filters.days || null } } }; } catch (error) { throw error; } } /** * 导入数据 */ static async importData(prices) { try { if (!Array.isArray(prices) || prices.length === 0) { throw new Error('无效的数据格式'); } // 验证并清洗数据 const validation = validateAndCleanPricesData(prices); if (validation.validCount === 0) { return { success: false, message: '没有有效的数据可以导入', data: { total: validation.total, imported: 0, errors: validation.errors } }; } // 批量插入有效数据 const result = await Price.batchInsert(validation.validData); return { success: true, message: `成功导入 ${result} 条数据${validation.errorCount > 0 ? `,${validation.errorCount} 条数据因格式错误被跳过` : ''}`, data: { imported: result, total: validation.total, validCount: validation.validCount, errorCount: validation.errorCount, errors: validation.errorCount > 0 ? validation.errors.slice(0, 10) : undefined // 只返回前 10 个错误 } }; } catch (error) { throw error; } } } module.exports = PriceService;