You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

331 lines
7.4 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/**
* 统计分析控制器
*
* 功能:处理数据分析和报表的业务逻辑
*/
const Analytics = require('../models/Analytics');
const { success } = require('../utils/response');
const { ValidationError } = require('../middleware/errorHandler');
/**
* 获取综合仪表盘数据
* GET /api/analytics/dashboard
*/
const getDashboard = async (req, res, next) => {
try {
const { start_date, end_date } = req.query;
const dashboard = await Analytics.getDashboard(
start_date || null,
end_date || null
);
res.json(success(dashboard, '获取仪表盘数据成功'));
} catch (err) {
next(err);
}
};
/**
* 获取销售趋势
* GET /api/analytics/sales-trend
*/
const getSalesTrend = async (req, res, next) => {
try {
const { start_date, end_date } = req.query;
console.log('📊 getSalesTrend 请求参数:', { start_date, end_date });
if (!start_date || !end_date) {
throw new ValidationError('请提供开始日期和结束日期');
}
const trend = await Analytics.getSalesTrend(start_date, end_date);
console.log('📊 Model返回数据条数:', trend ? trend.length : 0);
console.log('📊 数据样例:', trend ? trend.slice(0, 2) : 'null');
res.json(success({
period: { start_date, end_date },
data: trend
}, '获取销售趋势成功'));
} catch (err) {
console.error('❌ getSalesTrend错误:', err);
next(err);
}
};
/**
* 获取热门菜品排行
* GET /api/analytics/top-dishes
*/
const getTopDishes = async (req, res, next) => {
try {
const { limit = 10, start_date, end_date } = req.query;
const dishes = await Analytics.getTopDishes(
parseInt(limit),
start_date || null,
end_date || null
);
res.json(success({
count: dishes.length,
dishes
}, '获取热门菜品成功'));
} catch (err) {
next(err);
}
};
/**
* 获取分类销售统计
* GET /api/analytics/category-sales
*/
const getCategorySales = async (req, res, next) => {
try {
const { start_date, end_date } = req.query;
const categories = await Analytics.getCategorySales(
start_date || null,
end_date || null
);
res.json(success({
count: categories.length,
categories
}, '获取分类销售统计成功'));
} catch (err) {
next(err);
}
};
/**
* 获取营业时段分析
* GET /api/analytics/hourly-analysis
*/
const getHourlyAnalysis = async (req, res, next) => {
try {
const { start_date, end_date } = req.query;
const hourlyData = await Analytics.getHourlyAnalysis(
start_date || null,
end_date || null
);
res.json(success({
data: hourlyData
}, '获取时段分析成功'));
} catch (err) {
next(err);
}
};
/**
* 获取会员消费排行
* GET /api/analytics/top-members
*/
const getTopMembers = async (req, res, next) => {
try {
const { limit = 10, start_date, end_date } = req.query;
const members = await Analytics.getTopMembers(
parseInt(limit),
start_date || null,
end_date || null
);
res.json(success({
count: members.length,
members
}, '获取会员排行成功'));
} catch (err) {
next(err);
}
};
/**
* 获取订单类型统计
* GET /api/analytics/order-types
*/
const getOrderTypeStats = async (req, res, next) => {
try {
const { start_date, end_date } = req.query;
const orderTypes = await Analytics.getOrderTypeStats(
start_date || null,
end_date || null
);
res.json(success({
data: orderTypes
}, '获取订单类型统计成功'));
} catch (err) {
next(err);
}
};
/**
* 获取支付方式统计
* GET /api/analytics/payment-methods
*/
const getPaymentMethodStats = async (req, res, next) => {
try {
const { start_date, end_date } = req.query;
const paymentMethods = await Analytics.getPaymentMethodStats(
start_date || null,
end_date || null
);
res.json(success({
data: paymentMethods
}, '获取支付方式统计成功'));
} catch (err) {
next(err);
}
};
/**
* 获取月度对比报表
* GET /api/analytics/monthly-comparison
*/
const getMonthlyComparison = async (req, res, next) => {
try {
const { months = 6 } = req.query;
const monthlyData = await Analytics.getMonthlyComparison(parseInt(months));
res.json(success({
months: parseInt(months),
data: monthlyData
}, '获取月度对比成功'));
} catch (err) {
next(err);
}
};
/**
* 获取库存价值报表
* GET /api/analytics/inventory-value
*/
const getInventoryValue = async (req, res, next) => {
try {
const inventoryValue = await Analytics.getInventoryValue();
res.json(success(inventoryValue, '获取库存价值报表成功'));
} catch (err) {
next(err);
}
};
/**
* 获取优惠券使用统计
* GET /api/analytics/coupon-stats
*/
const getCouponStats = async (req, res, next) => {
try {
const { start_date, end_date } = req.query;
const couponStats = await Analytics.getCouponStats(
start_date || null,
end_date || null
);
res.json(success({
count: couponStats.length,
coupons: couponStats
}, '获取优惠券统计成功'));
} catch (err) {
next(err);
}
};
/**
* 获取预订转化率
* GET /api/analytics/reservation-conversion
*/
const getReservationConversion = async (req, res, next) => {
try {
const { start_date, end_date } = req.query;
const conversion = await Analytics.getReservationConversion(
start_date || null,
end_date || null
);
res.json(success(conversion, '获取预订转化率成功'));
} catch (err) {
next(err);
}
};
/**
* 导出综合报表CSV格式
* GET /api/analytics/export
*/
const exportReport = async (req, res, next) => {
try {
const { start_date, end_date, type = 'sales' } = req.query;
if (!start_date || !end_date) {
throw new ValidationError('请提供开始日期和结束日期');
}
let data;
let filename;
switch (type) {
case 'sales':
data = await Analytics.getSalesTrend(start_date, end_date);
filename = `sales_report_${start_date}_${end_date}.csv`;
break;
case 'dishes':
data = await Analytics.getTopDishes(100, start_date, end_date);
filename = `dishes_report_${start_date}_${end_date}.csv`;
break;
case 'members':
data = await Analytics.getTopMembers(100, start_date, end_date);
filename = `members_report_${start_date}_${end_date}.csv`;
break;
default:
throw new ValidationError('无效的报表类型');
}
// 生成CSV内容
if (data.length === 0) {
throw new ValidationError('该时间段内没有数据');
}
const headers = Object.keys(data[0]).join(',');
const rows = data.map(row => Object.values(row).join(',')).join('\n');
const csv = `${headers}\n${rows}`;
// 设置响应头
res.setHeader('Content-Type', 'text/csv; charset=utf-8');
res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
res.send('\ufeff' + csv); // 添加BOM以支持中文
} catch (err) {
next(err);
}
};
// 导出所有控制器函数
module.exports = {
getDashboard,
getSalesTrend,
getTopDishes,
getCategorySales,
getHourlyAnalysis,
getTopMembers,
getOrderTypeStats,
getPaymentMethodStats,
getMonthlyComparison,
getInventoryValue,
getCouponStats,
getReservationConversion,
exportReport
};