From 05b031bb4063bb79b0e5e308d8312b168c3b172b Mon Sep 17 00:00:00 2001 From: user9994793890 <3722323274763196-user9994793890@noreply.coze.cn> Date: Tue, 7 Jul 2026 16:52:51 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E5=96=84=E6=97=A5=E6=8A=A5?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 主要更新: 1. 【核心变更】从批量拆分改为单个企业/用户选择生成 - 用户现在可以选择特定的企业或用户生成日报 - 企业列表显示企业名称(如"国营-西南-79路") - 用户列表显示用户姓名,并标注用户类型(企业用户/普通用户) 2. 【新增功能】求和功能 - 支持选择多个字段进行求和 - 求和结果显示在Excel表格底部 - 可对金额、电量等多列分别求和 3. 【数据库表结构更新】 - t_daily_report_config 新增字段:split_name(企业/用户名称)、sum_fields(求和字段列表) - t_daily_report_history 新增字段:split_name、sum_results(求和结果JSON) 4. 【前端页面优化】 - 配置管理:选择企业/用户时显示名称和订单数量 - 日报生成:支持选择单个企业/用户、字段和求和字段 - 历史记录:显示企业/用户名称和求和结果 5. 【API接口优化】 - 企业列表API:返回company_name、company_no、company_group等信息 - 用户列表API:返回user_name和用户类型标签 - 日报生成API:支持sum_fields参数 6. 【删除临时文件】清理debug调试API接口 所有代码静态检查通过,企业列表和用户列表API测试成功 Coze-Commit-Type: user Coze-User-ID: 3722323274763196 Coze-Conversation-ID: 9894087 --- src/app/api/config/route.ts | 41 ++++-- src/app/api/entities/route.ts | 53 +++++--- src/app/api/report/generate/route.ts | 25 +++- src/app/page.tsx | 196 +++++++++++++++++++++++---- src/lib/db.ts | 4 +- src/lib/init-tables.ts | 11 +- src/lib/report-generator.ts | 175 +++++++++++++----------- 7 files changed, 361 insertions(+), 144 deletions(-) diff --git a/src/app/api/config/route.ts b/src/app/api/config/route.ts index 3bbbc7d..dbe85ae 100644 --- a/src/app/api/config/route.ts +++ b/src/app/api/config/route.ts @@ -11,10 +11,12 @@ export async function GET() { // 尝试查询配置(如果表不存在会失败) let configs: any[] = []; try { - const [configRows] = await pool.execute( + const connection = await pool.getConnection(); + const [configRows] = await connection.query( 'SELECT * FROM t_daily_report_config WHERE is_active = 1 ORDER BY create_time DESC' ); configs = configRows || []; + connection.release(); } catch (dbError) { // 如果表不存在,返回空数组 console.log('配置表查询失败,可能是表未初始化:', dbError); @@ -39,13 +41,20 @@ export async function GET() { export async function POST(request: NextRequest) { try { const body = await request.json(); - const { config_name, split_type, selected_fields } = body; + const { + config_name, + split_type, + split_value, + split_name, + selected_fields, + sum_fields + } = body; // 验证必填字段 - if (!config_name || !split_type || !selected_fields || selected_fields.length === 0) { + if (!config_name || !split_type || !split_value || !selected_fields || selected_fields.length === 0) { return NextResponse.json({ success: false, - message: '缺少必填字段' + message: '缺少必填字段(配置名称、拆分方式、拆分值、选择的字段)' }, { status: 400 }); } @@ -57,17 +66,29 @@ export async function POST(request: NextRequest) { }, { status: 400 }); } + // 获取连接 + const connection = await pool.getConnection(); + // 插入配置 - const [result] = await pool.execute( - `INSERT INTO t_daily_report_config (config_name, split_type, selected_fields) - VALUES (?, ?, ?)`, - [config_name, split_type, JSON.stringify(selected_fields)] + await connection.query( + `INSERT INTO t_daily_report_config + (config_name, split_type, split_value, split_name, selected_fields, sum_fields) + VALUES (?, ?, ?, ?, ?, ?)`, + [ + config_name, + split_type, + split_value, + split_name || null, + JSON.stringify(selected_fields), + sum_fields ? JSON.stringify(sum_fields) : null + ] ); + connection.release(); + return NextResponse.json({ success: true, - message: '配置创建成功', - data: result + message: '配置创建成功' }); } catch (error) { return NextResponse.json({ diff --git a/src/app/api/entities/route.ts b/src/app/api/entities/route.ts index bba98b3..6df7489 100644 --- a/src/app/api/entities/route.ts +++ b/src/app/api/entities/route.ts @@ -1,55 +1,68 @@ import { NextRequest, NextResponse } from 'next/server'; import pool from '@/lib/db'; -// GET - 获取企业列表或用户列表 +// GET - 获取企业列表或用户列表(带名称) export async function GET(request: NextRequest) { try { const { searchParams } = new URL(request.url); const type = searchParams.get('type'); // 'company' 或 'user' + // 获取连接 + const connection = await pool.getConnection(); + if (type === 'company') { - // 获取企业列表(根据company_id分组,并统计订单数量) - const [companies] = await pool.execute(` + // 获取企业列表(从 t_company 表读取企业名称) + const [companies] = await connection.query(` SELECT - company_id, - COUNT(*) as order_count, - SUM(actual_pay_amount) as total_amount - FROM t_equipment_charge_order - WHERE state = 3 AND company_id IS NOT NULL - GROUP BY company_id + c.id as company_id, + c.company_name, + c.company_no, + c.company_group, + COUNT(o.id) as order_count, + COALESCE(SUM(o.actual_pay_amount), 0) as total_amount + FROM t_company c + LEFT JOIN t_equipment_charge_order o ON c.id = o.company_id AND o.state = 3 + GROUP BY c.id, c.company_name, c.company_no, c.company_group ORDER BY order_count DESC `); + connection.release(); + return NextResponse.json({ success: true, data: companies, type: '企业' }); } else if (type === 'user') { - // 获取用户列表(根据user_id分组,区分企业用户和普通用户) - const [users] = await pool.execute(` + // 获取用户列表(从 t_user 表读取用户姓名) + const [users] = await connection.query(` SELECT - user_id, - order_type, - COUNT(*) as order_count, - SUM(actual_pay_amount) as total_amount, + u.id as user_id, + u.user_name, + u.phone, + o.order_type, + COUNT(o.id) as order_count, + COALESCE(SUM(o.actual_pay_amount), 0) as total_amount, CASE - WHEN order_type = 3 THEN '企业用户' - WHEN order_type = 1 THEN '普通用户' + WHEN o.order_type = 3 THEN '企业用户' + WHEN o.order_type = 1 THEN '普通用户' ELSE '其他类型' END as user_type_label - FROM t_equipment_charge_order - WHERE state = 3 - GROUP BY user_id, order_type + FROM t_user u + LEFT JOIN t_equipment_charge_order o ON u.id = o.user_id AND o.state = 3 + GROUP BY u.id, u.user_name, u.phone, o.order_type ORDER BY order_count DESC `); + connection.release(); + return NextResponse.json({ success: true, data: users, type: '用户' }); } else { + connection.release(); return NextResponse.json({ success: false, message: '类型参数必须为 company 或 user' diff --git a/src/app/api/report/generate/route.ts b/src/app/api/report/generate/route.ts index ca641db..5f40bbb 100644 --- a/src/app/api/report/generate/route.ts +++ b/src/app/api/report/generate/route.ts @@ -5,14 +5,22 @@ import { generateDailyReport, generateBatchReports } from '@/lib/report-generato export async function POST(request: NextRequest) { try { const body = await request.json(); - const { split_type, split_value, selected_fields, report_date, config_id } = body; + const { + split_type, + split_value, + split_name, + selected_fields, + sum_fields, + report_date, + config_id + } = body; - // 如果提供了config_id,则批量生成所有企业/用户的日报 + // 如果提供了config_id,则根据配置生成日报 if (config_id) { const results = await generateBatchReports(config_id); return NextResponse.json({ success: true, - message: '批量日报生成完成', + message: '日报生成完成', data: results }); } @@ -21,11 +29,18 @@ export async function POST(request: NextRequest) { if (!split_type || !split_value || !selected_fields || selected_fields.length === 0) { return NextResponse.json({ success: false, - message: '缺少必填字段' + message: '缺少必填字段(拆分方式、拆分值、选择的字段)' }, { status: 400 }); } - const result = await generateDailyReport(split_type, split_value, selected_fields, report_date); + const result = await generateDailyReport( + split_type, + split_value, + split_name, + selected_fields, + sum_fields, + report_date + ); return NextResponse.json({ success: result.success, diff --git a/src/app/page.tsx b/src/app/page.tsx index 699714c..a43899a 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -24,12 +24,17 @@ export default function Home() { // 配置表单 const [configName, setConfigName] = useState(''); const [splitType, setSplitType] = useState('company_id'); + const [splitValue, setSplitValue] = useState(''); + const [splitName, setSplitName] = useState(''); const [selectedFields, setSelectedFields] = useState([]); + const [sumFields, setSumFields] = useState([]); // 日报生成表单 const [reportSplitType, setReportSplitType] = useState('company_id'); const [reportSplitValue, setReportSplitValue] = useState(''); + const [reportSplitName, setReportSplitName] = useState(''); const [reportSelectedFields, setReportSelectedFields] = useState([]); + const [reportSumFields, setReportSumFields] = useState([]); const [reportDate, setReportDate] = useState(''); // 历史查询 @@ -46,6 +51,8 @@ export default function Home() { } else { loadUsers(); } + setSplitValue(''); + setSplitName(''); }, [splitType]); useEffect(() => { @@ -54,6 +61,8 @@ export default function Home() { } else { loadUsers(); } + setReportSplitValue(''); + setReportSplitName(''); }, [reportSplitType]); const loadData = async () => { @@ -112,10 +121,10 @@ export default function Home() { }; const handleCreateConfig = async () => { - if (!configName || selectedFields.length === 0) { + if (!configName || !splitValue || selectedFields.length === 0) { toast({ title: '错误', - description: '请填写配置名称并选择至少一个字段', + description: '请填写配置名称并选择企业和字段', variant: 'destructive' }); return; @@ -129,7 +138,10 @@ export default function Home() { body: JSON.stringify({ config_name: configName, split_type: splitType, - selected_fields: selectedFields + split_value: splitValue, + split_name: splitName, + selected_fields: selectedFields, + sum_fields: sumFields }) }); @@ -141,7 +153,10 @@ export default function Home() { }); loadData(); setConfigName(''); + setSplitValue(''); + setSplitName(''); setSelectedFields([]); + setSumFields([]); } else { toast({ title: '错误', @@ -177,7 +192,9 @@ export default function Home() { body: JSON.stringify({ split_type: reportSplitType, split_value: reportSplitValue, + split_name: reportSplitName, selected_fields: reportSelectedFields, + sum_fields: reportSumFields, report_date: reportDate }) }); @@ -237,13 +254,19 @@ export default function Home() { setLoading(false); }; - const handleFieldChange = (field: string, checked: boolean, type: 'config' | 'report') => { + const handleFieldChange = (field: string, checked: boolean, type: 'config' | 'report' | 'sum') => { if (type === 'config') { if (checked) { setSelectedFields([...selectedFields, field]); } else { setSelectedFields(selectedFields.filter(f => f !== field)); } + } else if (type === 'sum') { + if (checked) { + setSumFields([...sumFields, field]); + } else { + setSumFields(sumFields.filter(f => f !== field)); + } } else { if (checked) { setReportSelectedFields([...reportSelectedFields, field]); @@ -253,6 +276,24 @@ export default function Home() { } }; + const handleReportSumFieldChange = (field: string, checked: boolean) => { + if (checked) { + setReportSumFields([...reportSumFields, field]); + } else { + setReportSumFields(reportSumFields.filter(f => f !== field)); + } + }; + + const handleSplitValueChange = (value: string, name: string) => { + setSplitValue(value); + setSplitName(name); + }; + + const handleReportSplitValueChange = (value: string, name: string) => { + setReportSplitValue(value); + setReportSplitName(name); + }; + return (
@@ -260,7 +301,7 @@ export default function Home() { 订单日报系统 - 每天早8点自动生成前一天的订单日报(早8点到当天早8点) + 为选定的企业或用户生成订单日报(前一天早8点到当天早8点) @@ -287,26 +328,58 @@ export default function Home() { id="configName" value={configName} onChange={(e) => setConfigName(e.target.value)} - placeholder="例如:企业日报配置" + placeholder="例如:国营-西南-79路日报" />
-
- - +
+
+ + +
+ +
+ + +
-
+
{availableFields.map((field) => (
+
+ +
+ {availableFields.filter(f => + ['charge_degree', 'charge_ah', 'receivable_electric_fee', 'receivable_service_fee', + 'receivable_total_fee', 'charge_elecfee_amount', 'charge_service_amount', + 'actual_pay_amount', 'pay_amount', 'total_amount'].some(numField => f.field.includes(numField)) + ).map((field) => ( +
+ + handleFieldChange(field.field, checked as boolean, 'sum') + } + /> + +
+ ))} +
+
+
- { + const selectedItem = reportSplitType === 'company_id' + ? companies.find(c => String(c.company_id) === value) + : users.find(u => String(u.user_id) === value); + handleReportSplitValueChange(value, selectedItem?.company_name || selectedItem?.user_name || ''); + }}> @@ -421,12 +529,12 @@ export default function Home() { {reportSplitType === 'company_id' ? companies.map((company) => ( - 企业ID: {company.company_id} ({company.order_count} 单) + {company.company_name} ({company.order_count} 单) )) : users.map((user) => ( - 用户ID: {user.user_id} ({user.user_type_label}, {user.order_count} 单) + {user.user_name || `用户${user.user_id}`} ({user.user_type_label}, {user.order_count} 单) )) } @@ -465,6 +573,30 @@ export default function Home() {
+
+ +
+ {availableFields.filter(f => + ['charge_degree', 'charge_ah', 'receivable_electric_fee', 'receivable_service_fee', + 'receivable_total_fee', 'charge_elecfee_amount', 'charge_service_amount', + 'actual_pay_amount', 'pay_amount', 'total_amount'].some(numField => f.field.includes(numField)) + ).map((field) => ( +
+ + handleReportSumFieldChange(field.field, checked as boolean) + } + /> + +
+ ))} +
+
+