Files
ylt_diy/lib/report_generator.py
user9994793890 9fa2745daa fix: 修复日报生成后历史记录不显示的问题
问题:生成日报后,历史记录中没有显示订单。

原因:
1. 如果时间范围内没有订单数据,函数直接返回失败,没有保存失败记录
2. 缺少详细的日志输出,难以诊断问题

修复内容:

1. lib/report_generator.py:
   - 即使没有订单数据,也保存失败记录到历史表(status=0)
   - 添加详细的日志输出:
     * 开始生成时的配置信息
     * 查询到的订单数量
     * 生成成功的详细信息
     * 生成失败的错误堆栈
   - 改进异常处理,确保失败记录一定被保存

2. 新增 test_diagnosis.py:
   - 自动检查数据库连接
   - 检查表是否存在
   - 查看配置和历史记录
   - 检查订单数据统计
   - 提供诊断建议

3. 新增 诊断指南.md:
   - 常见问题和解决方案
   - 手动检查数据库的SQL语句
   - 调试技巧和API测试方法

现在用户可以:
1. 运行 python test_diagnosis.py 快速诊断问题
2. 查看服务器控制台的详细日志
3. 参考诊断指南排查问题
4. 即使生成失败,也能在历史记录中看到记录

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

313 lines
11 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
)
# 计算总金额
total_amount = sum(float(order.get('total_money', 0) or 0) for order in orders)
# 保存历史记录
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"[日报生成] ✓ 生成成功")
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)
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
)
execute_insert(sql, params)
return history_id