import pool from '@/lib/db'; import { FIELD_MAPPING, STATE_MAPPING, ORDER_TYPE_MAPPING, PAY_METHOD_MAPPING, PAY_STATUS_MAPPING } from '@/lib/field-mapping'; import * as XLSX from 'xlsx'; import { writeFileSync, mkdirSync } from 'fs'; import path from 'path'; // 日报生成配置 interface ReportConfig { config_id: number; split_type: string; // 'company_id' 或 'user_id' selected_fields: string[]; // 用户选择的字段 } // 日报生成结果 interface ReportResult { success: boolean; reportDate: string; startTime: string; endTime: string; splitType: string; splitValue: string; totalOrders: number; totalAmount: number; filePath?: string; error?: string; } // 生成日报 export async function generateDailyReport( splitType: string, splitValue: string, selectedFields: string[], reportDate?: string // 可选的报表日期,默认为昨天 ): Promise { try { // 计算时间范围:前一天早8点到当天早8点 const targetDate = reportDate ? new Date(reportDate) : new Date(); targetDate.setDate(targetDate.getDate() - 1); // 昨天 const startTime = new Date(targetDate); startTime.setHours(8, 0, 0, 0); const endTime = new Date(targetDate); endTime.setDate(endTime.getDate() + 1); endTime.setHours(8, 0, 0, 0); // 查询订单数据 const queryField = splitType === 'company_id' ? 'company_id' : 'user_id'; const fieldList = selectedFields.join(', '); const [orders] = await pool.execute( `SELECT ${fieldList} FROM t_equipment_charge_order WHERE state = 3 AND report_time >= ? AND report_time < ? AND ${queryField} = ? ORDER BY report_time ASC`, [startTime, endTime, splitValue] ); if (!Array.isArray(orders) || orders.length === 0) { return { success: false, reportDate: targetDate.toISOString().split('T')[0], startTime: startTime.toISOString(), endTime: endTime.toISOString(), splitType, splitValue, totalOrders: 0, totalAmount: 0, error: '该时间段内没有订单数据' }; } // 处理数据:将字段名转换为中文,并转换特殊字段的值 const processedOrders = orders.map(order => { const processed: Record = {}; for (const [key, value] of Object.entries(order)) { // 使用中文字段名 const chineseName = FIELD_MAPPING[key] || key; // 特殊字段值转换 if (key === 'state' && typeof value === 'number') { processed[chineseName] = STATE_MAPPING[value] || value; } else if (key === 'order_type' && typeof value === 'number') { processed[chineseName] = ORDER_TYPE_MAPPING[value] || value; } else if (key === 'pay_method' && typeof value === 'number') { processed[chineseName] = PAY_METHOD_MAPPING[value] || value; } else if (key === 'pay_status' && typeof value === 'number') { processed[chineseName] = PAY_STATUS_MAPPING[value] || value; } else { processed[chineseName] = value; } } return processed; }); // 生成Excel文件 const workbook = XLSX.utils.book_new(); const worksheet = XLSX.utils.json_to_sheet(processedOrders); // 设置列宽 const columnWidths = selectedFields.map(field => ({ wch: Math.max(FIELD_MAPPING[field]?.length || 10, 15) })); worksheet['!cols'] = columnWidths; XLSX.utils.book_append_sheet(workbook, worksheet, '日报'); // 生成文件名 const fileName = `日报_${splitType === 'company_id' ? '企业' : '用户'}_${splitValue}_${targetDate.toISOString().split('T')[0]}.xlsx`; const reportsDir = path.join(process.cwd(), 'public', 'reports'); // 确保目录存在 mkdirSync(reportsDir, { recursive: true }); const filePath = path.join(reportsDir, fileName); // 写入文件 writeFileSync(filePath, XLSX.write(workbook, { type: 'buffer', bookType: 'xlsx' })); // 计算统计信息 const totalOrders = orders.length; const totalAmount = orders.reduce((sum: number, order: any) => { return sum + (parseFloat(order.actual_pay_amount) || 0); }, 0); return { success: true, reportDate: targetDate.toISOString().split('T')[0], startTime: startTime.toISOString(), endTime: endTime.toISOString(), splitType, splitValue, totalOrders, totalAmount, filePath: `/reports/${fileName}` }; } catch (error) { return { success: false, reportDate: '', startTime: '', endTime: '', splitType, splitValue, totalOrders: 0, totalAmount: 0, error: error instanceof Error ? error.message : '未知错误' }; } } // 批量生成日报(用于定时任务) export async function generateBatchReports(configId: number): Promise { try { // 获取配置 const [configs] = await pool.execute( 'SELECT * FROM t_daily_report_config WHERE id = ? AND is_active = 1', [configId] ); if (!Array.isArray(configs) || configs.length === 0) { throw new Error('配置不存在或已禁用'); } const config = configs[0]; const splitType = config.split_type; const selectedFields = JSON.parse(config.selected_fields); // 获取所有需要生成日报的企业/用户列表 const queryField = splitType === 'company_id' ? 'company_id' : 'user_id'; // 计算时间范围 const targetDate = new Date(); targetDate.setDate(targetDate.getDate() - 1); const startTime = new Date(targetDate); startTime.setHours(8, 0, 0, 0); const endTime = new Date(targetDate); endTime.setDate(endTime.getDate() + 1); endTime.setHours(8, 0, 0, 0); // 获取该时间段内的所有企业/用户 const [entities] = await pool.execute( `SELECT DISTINCT ${queryField} as entity_id FROM t_equipment_charge_order WHERE state = 3 AND report_time >= ? AND report_time < ? AND ${queryField} IS NOT NULL`, [startTime, endTime] ); if (!Array.isArray(entities) || entities.length === 0) { return []; } // 批量生成日报 const results: ReportResult[] = []; for (const entity of entities) { const entityId = entity.entity_id; const result = await generateDailyReport(splitType, String(entityId), selectedFields); // 保存到历史记录表 if (result.success) { await pool.execute( `INSERT INTO t_daily_report_history (report_date, start_time, end_time, split_type, split_value, config_id, total_orders, total_amount, file_path, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [ result.reportDate, result.startTime, result.endTime, result.splitType, result.splitValue, configId, result.totalOrders, result.totalAmount, result.filePath || null, 1 ] ); } else { await pool.execute( `INSERT INTO t_daily_report_history (report_date, start_time, end_time, split_type, split_value, config_id, total_orders, total_amount, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [ result.reportDate, result.startTime, result.endTime, result.splitType, result.splitValue, configId, result.totalOrders, result.totalAmount, 0 ] ); } results.push(result); } return results; } catch (error) { throw error; } }