Files
ylt_diy/lib/report_generator.py
user9994793890 2ad6e27131 debug: 添加详细调试日志,诊断下载按钮不显示的问题
问题:日报生成成功后,历史记录中没有下载按钮。

改进内容:

1. lib/report_generator.py:
   - 在生成Excel文件后添加日志:显示文件路径
   - 在保存历史记录前添加日志:显示file_path值
   - 在save_report_history函数中添加详细日志:
     * 显示准备保存的信息
     * 显示file_path和status的值
     * 显示SQL执行结果

2. app.py:
   - 在历史记录API中添加调试日志
   - 显示每条记录的status和file_path
   - 确保file_path不为None(转换为空字符串)
   - 添加错误堆栈输出

3. templates/index.html:
   - 在渲染历史记录时添加console.log调试
   - 显示每条记录的详细信息
   - 显示status和file_path的类型
   - 改进下载按钮判断逻辑
   - 添加title提示显示文件路径

4. 新增文档:
   - 查看调试日志.md:详细的调试步骤和问题诊断方法

现在用户可以通过日志准确定位问题:
- 服务器控制台:查看后端日志
- 浏览器控制台:查看前端日志
- curl测试:直接查看API返回数据

Coze-Commit-Type: user
Coze-User-ID: 3722323274763196
Coze-Conversation-ID: 9894087
2026-07-09 12:59:40 +08:00

324 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
日报生成核心逻辑
"""
import os
import json
from datetime import datetime, timedelta
from openpyxl import Workbook
from openpyxl.styles import Font, Alignment, PatternFill
import pytz
from lib.db import execute_query, execute_insert
from lib.field_mapping import get_field_display_name
def get_beijing_time():
"""获取北京时间"""
beijing_tz = pytz.timezone('Asia/Shanghai')
return datetime.now(beijing_tz)
def generate_daily_report(config_id, start_time=None, end_time=None):
"""
生成日报
Args:
config_id: 配置ID
start_time: 开始时间可选默认为昨天8点
end_time: 结束时间可选默认为今天8点
Returns:
dict: 生成结果,包含 success, message, file_path, total_orders, total_amount, sum_results
"""
try:
# 获取配置信息
configs = execute_query(
'SELECT * FROM t_daily_report_config WHERE id = %s',
(config_id,)
)
if not configs:
return {'success': False, 'message': '配置不存在'}
config = configs[0]
# 解析字段
selected_fields = json.loads(config['selected_fields'])
sum_fields = json.loads(config['sum_fields']) if config['sum_fields'] else []
# 设置时间范围
if not start_time or not end_time:
now = get_beijing_time()
end_time = now.replace(hour=8, minute=0, second=0, microsecond=0)
start_time = end_time - timedelta(days=1)
# 格式化时间
start_time_str = start_time.strftime('%Y-%m-%d %H:%M:%S')
end_time_str = end_time.strftime('%Y-%m-%d %H:%M:%S')
print(f"[日报生成] 开始生成日报")
print(f" 配置ID: {config_id}")
print(f" 配置名称: {config['config_name']}")
print(f" 拆分方式: {config['split_type']} = {config['split_value']}")
print(f" 时间范围: {start_time_str}{end_time_str}")
print(f" 选择字段: {len(selected_fields)}")
# 构建查询SQL
fields_str = ', '.join(selected_fields)
sql = f"""
SELECT {fields_str}
FROM t_equipment_charge_order
WHERE state = 3
AND report_time >= %s
AND report_time < %s
AND {config['split_type']} = %s
ORDER BY report_time DESC
"""
# 执行查询
orders = execute_query(sql, (start_time_str, end_time_str, config['split_value']))
print(f"[日报生成] 查询到 {len(orders)} 条订单")
if not orders:
# 即使没有数据,也保存一条失败记录
save_report_history(
report_date=start_time.strftime('%Y-%m-%d'),
start_time=start_time_str,
end_time=end_time_str,
split_type=config['split_type'],
split_value=config['split_value'],
split_name=config.get('split_name', config['split_value']),
config_id=config_id,
total_orders=0,
total_amount=0,
sum_results={},
file_path='',
status=0
)
return {
'success': False,
'message': f'时间范围内没有符合条件的订单数据(查询条件:{start_time_str}{end_time_str}{config["split_type"]}={config["split_value"]}state=3'
}
# 计算求和
sum_results = {}
for field in sum_fields:
if field in orders[0]:
total = sum(float(order.get(field, 0) or 0) for order in orders)
sum_results[field] = total
# 生成Excel文件
file_path = generate_excel(
orders=orders,
selected_fields=selected_fields,
sum_fields=sum_fields,
sum_results=sum_results,
config_name=config['config_name'],
split_name=config.get('split_name', config['split_value']),
start_time=start_time_str,
end_time=end_time_str
)
print(f"[日报生成] Excel文件已生成: {file_path}")
# 计算总金额
total_amount = sum(float(order.get('total_money', 0) or 0) for order in orders)
# 保存历史记录
print(f"[日报生成] 准备保存历史记录file_path={file_path}")
history_id = save_report_history(
report_date=start_time.strftime('%Y-%m-%d'),
start_time=start_time_str,
end_time=end_time_str,
split_type=config['split_type'],
split_value=config['split_value'],
split_name=config.get('split_name', config['split_value']),
config_id=config_id,
total_orders=len(orders),
total_amount=total_amount,
sum_results=sum_results,
file_path=file_path,
status=1
)
print(f"[日报生成] 历史记录已保存ID={history_id}")
print(f"[日报生成] ✓ 生成成功")
print(f" 订单数量: {len(orders)}")
print(f" 总金额: {total_amount}")
print(f" 文件路径: {file_path}")
print(f" 历史记录ID: {history_id}")
return {
'success': True,
'message': '日报生成成功',
'file_path': file_path,
'total_orders': len(orders),
'total_amount': total_amount,
'sum_results': sum_results,
'history_id': history_id
}
except Exception as e:
print(f"[日报生成] ✗ 生成失败: {str(e)}")
import traceback
traceback.print_exc()
# 保存失败记录
try:
save_report_history(
report_date=start_time.strftime('%Y-%m-%d') if start_time else datetime.now().strftime('%Y-%m-%d'),
start_time=start_time_str if 'start_time_str' in locals() else '',
end_time=end_time_str if 'end_time_str' in locals() else '',
split_type=config['split_type'] if 'config' in locals() and config else '',
split_value=config['split_value'] if 'config' in locals() and config else '',
split_name=config.get('split_name', '') if 'config' in locals() and config else '',
config_id=config_id,
total_orders=0,
total_amount=0,
sum_results={},
file_path='',
status=0
)
print(f"[日报生成] 已保存失败记录到历史表")
except Exception as save_error:
print(f"[日报生成] 保存失败记录时出错: {str(save_error)}")
return {
'success': False,
'message': f'生成日报失败: {str(e)}'
}
def generate_excel(orders, selected_fields, sum_fields, sum_results,
config_name, split_name, start_time, end_time):
"""
生成Excel文件
Returns:
str: 文件路径
"""
# 创建reports目录
reports_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'public', 'reports')
os.makedirs(reports_dir, exist_ok=True)
# 生成文件名
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
filename = f'daily_report_{config_name}_{split_name}_{timestamp}.xlsx'
file_path = os.path.join(reports_dir, filename)
# 创建工作簿
wb = Workbook()
ws = wb.active
ws.title = '日报'
# 写入标题
ws.merge_cells(start_row=1, start_column=1, end_row=1, end_column=len(selected_fields))
title_cell = ws.cell(row=1, column=1)
title_cell.value = f'{config_name} - {split_name} 日报'
title_cell.font = Font(size=16, bold=True)
title_cell.alignment = Alignment(horizontal='center')
# 写入时间范围
ws.merge_cells(start_row=2, start_column=1, end_row=2, end_column=len(selected_fields))
time_cell = ws.cell(row=2, column=1)
time_cell.value = f'统计时间: {start_time}{end_time}'
time_cell.alignment = Alignment(horizontal='center')
# 写入表头(中文)
header_row = 4
for col_idx, field in enumerate(selected_fields, 1):
cell = ws.cell(row=header_row, column=col_idx)
cell.value = get_field_display_name(field)
cell.font = Font(bold=True)
cell.fill = PatternFill(start_color='D3D3D3', end_color='D3D3D3', fill_type='solid')
cell.alignment = Alignment(horizontal='center')
# 写入数据
for row_idx, order in enumerate(orders, header_row + 1):
for col_idx, field in enumerate(selected_fields, 1):
cell = ws.cell(row=row_idx, column=col_idx)
value = order.get(field, '')
# 格式化时间字段
if field in ['report_time', 'start_time', 'end_time', 'create_time', 'update_time', 'pay_time']:
if isinstance(value, datetime):
value = value.strftime('%Y-%m-%d %H:%M:%S')
cell.value = value
# 写入求和行
if sum_fields:
sum_row = header_row + len(orders) + 1
ws.cell(row=sum_row, column=1).value = '合计'
ws.cell(row=sum_row, column=1).font = Font(bold=True)
for col_idx, field in enumerate(selected_fields, 1):
if field in sum_fields and field in sum_results:
cell = ws.cell(row=sum_row, column=col_idx)
cell.value = sum_results[field]
cell.font = Font(bold=True)
cell.number_format = '0.00'
# 调整列宽
for col_idx, field in enumerate(selected_fields, 1):
max_length = len(get_field_display_name(field))
for row in ws.iter_rows(min_row=header_row + 1, max_row=header_row + len(orders),
min_col=col_idx, max_col=col_idx):
for cell in row:
if cell.value:
max_length = max(max_length, len(str(cell.value)))
ws.column_dimensions[ws.cell(row=1, column=col_idx).column_letter].width = min(max_length + 2, 50)
# 保存文件
wb.save(file_path)
# 返回相对路径
return f'/reports/{filename}'
def save_report_history(report_date, start_time, end_time, split_type, split_value,
split_name, config_id, total_orders, total_amount,
sum_results, file_path, status):
"""
保存日报生成历史记录
Returns:
int: 历史记录ID
"""
# 生成ID
history_id = int(datetime.now().timestamp() * 1000)
print(f"[保存历史] 准备保存历史记录")
print(f" ID: {history_id}")
print(f" file_path: '{file_path}'")
print(f" status: {status}")
sql = """
INSERT INTO t_daily_report_history
(id, report_date, start_time, end_time, split_type, split_value, split_name,
config_id, total_orders, total_amount, sum_results, file_path, status, create_time)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, NOW())
"""
params = (
history_id,
report_date,
start_time,
end_time,
split_type,
split_value,
split_name,
config_id,
total_orders,
total_amount,
json.dumps(sum_results, ensure_ascii=False),
file_path,
status
)
print(f"[保存历史] 执行SQL插入...")
result = execute_insert(sql, params)
print(f"[保存历史] 插入完成返回ID: {result}")
return history_id