增加序号,配置导出,导入,求和采集数据导出

This commit is contained in:
2026-07-14 16:05:43 +08:00
parent fdd232989b
commit cc2214ccf6
9 changed files with 587 additions and 42 deletions

View File

@@ -2,6 +2,7 @@
日报求和数据采集与管理
"""
import json
import os
from datetime import datetime
from lib.db import execute_query, execute_update, execute_insert
from lib.field_mapping import get_field_display_name
@@ -35,6 +36,7 @@ def collect_sum_data_from_history(history_id=None):
collected_count = 0
skipped_count = 0
config_sort_cache = {} # 缓存配置的排序号
for history in history_list:
sum_results = history.get('sum_results')
@@ -45,6 +47,24 @@ def collect_sum_data_from_history(history_id=None):
elif sum_results is None:
sum_results = {}
# 获取配置的排序号(缓存)
config_id = history.get('config_id')
sort_order = None
if config_id:
if config_id in config_sort_cache:
sort_order = config_sort_cache[config_id]
else:
try:
cfg_result = execute_query(
'SELECT sort_order FROM t_daily_report_config WHERE id = %s LIMIT 1',
(config_id,)
)
if cfg_result and cfg_result[0]['sort_order'] is not None:
sort_order = cfg_result[0]['sort_order']
config_sort_cache[config_id] = sort_order
except Exception:
config_sort_cache[config_id] = None
# 强制确保有充电电量(charge_degree)
if 'charge_degree' not in sum_results or sum_results['charge_degree'] is None:
# 从订单表重新计算充电电量
@@ -75,9 +95,9 @@ def collect_sum_data_from_history(history_id=None):
sql = '''
INSERT INTO t_daily_report_sum_data
(id, report_date, config_id, config_name, split_type, split_value, split_name,
sum_field_key, sum_field_name, sum_value, total_orders, data_source, remark,
sum_field_key, sum_field_name, sum_value, total_orders, sort_order, data_source, remark,
create_time, update_time)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, NOW(), NOW())
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, NOW(), NOW())
'''
params = (
sum_data_id,
@@ -91,6 +111,7 @@ def collect_sum_data_from_history(history_id=None):
get_field_display_name(field_key),
float(field_value) if field_value is not None else 0,
history.get('total_orders', 0),
sort_order,
'auto',
f'从历史记录自动采集,历史ID: {history["id"]}'
)
@@ -161,7 +182,7 @@ def _calculate_charge_degree(history):
return None
def get_sum_data_list(page=1, page_size=20, report_date=None, config_id=None,
def get_sum_data_list(page=1, page_size=20, start_date=None, end_date=None, config_id=None,
config_name=None, sum_field_key=None, data_source=None):
"""
查询求和数据列表
@@ -169,7 +190,8 @@ def get_sum_data_list(page=1, page_size=20, report_date=None, config_id=None,
Args:
page: 页码
page_size: 每页大小
report_date: 报表日期(可选)
start_date: 开始日期(可选)
end_date: 结束日期(可选)
config_id: 配置ID(可选)
config_name: 配置名称(可选,模糊搜索)
sum_field_key: 求和字段键名(可选)
@@ -184,9 +206,13 @@ def get_sum_data_list(page=1, page_size=20, report_date=None, config_id=None,
where_clauses = ['1=1']
params = []
if report_date:
where_clauses.append('report_date = %s')
params.append(report_date)
if start_date:
where_clauses.append('report_date >= %s')
params.append(start_date)
if end_date:
where_clauses.append('report_date <= %s')
params.append(end_date)
if config_id:
where_clauses.append('config_id = %s')
@@ -217,7 +243,7 @@ def get_sum_data_list(page=1, page_size=20, report_date=None, config_id=None,
list_result = execute_query(
f'''SELECT * FROM t_daily_report_sum_data
WHERE {where_sql}
ORDER BY report_date DESC, config_id, id DESC
ORDER BY report_date DESC, sort_order ASC, config_id ASC, id DESC
LIMIT %s OFFSET %s''',
tuple(params) + (page_size, offset)
)
@@ -249,12 +275,27 @@ def add_sum_data(data):
"""
try:
sum_data_id = int(datetime.now().timestamp() * 1000)
# 获取配置的排序号
sort_order = None
config_id = data.get('config_id')
if config_id:
try:
cfg_result = execute_query(
'SELECT sort_order FROM t_daily_report_config WHERE id = %s LIMIT 1',
(config_id,)
)
if cfg_result and cfg_result[0]['sort_order'] is not None:
sort_order = cfg_result[0]['sort_order']
except Exception:
pass
sql = '''
INSERT INTO t_daily_report_sum_data
(id, report_date, config_id, config_name, split_type, split_value, split_name,
sum_field_key, sum_field_name, sum_value, total_orders, data_source, remark,
sum_field_key, sum_field_name, sum_value, total_orders, sort_order, data_source, remark,
create_time, update_time)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, NOW(), NOW())
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, NOW(), NOW())
'''
params = (
sum_data_id,
@@ -268,6 +309,7 @@ def add_sum_data(data):
data.get('sum_field_name', ''),
float(data.get('sum_value', 0)),
data.get('total_orders', 0),
sort_order,
'manual',
data.get('remark', '')
)
@@ -409,13 +451,176 @@ def batch_delete_sum_data(ids):
return {'success': False, 'message': str(e)}
def export_sum_data_to_excel(start_date=None, end_date=None, config_id=None, data_source=None, keyword=None):
"""
导出求和数据为Excel
Args:
start_date: 开始日期(可选)
end_date: 结束日期(可选)
config_id: 配置ID(可选)
data_source: 数据来源(可选)
keyword: 配置名称关键词(可选)
Returns:
dict: {success, message, file_path}
"""
try:
from openpyxl import Workbook
from openpyxl.styles import Font, Alignment, PatternFill, Border, Side
# 构建查询条件
where_clauses = []
params = []
if start_date:
where_clauses.append('report_date >= %s')
params.append(start_date)
if end_date:
where_clauses.append('report_date <= %s')
params.append(end_date)
if config_id:
where_clauses.append('config_id = %s')
params.append(config_id)
if data_source:
where_clauses.append('data_source = %s')
params.append(data_source)
if keyword:
where_clauses.append('config_name LIKE %s')
params.append(f'%{keyword}%')
where_sql = ''
if where_clauses:
where_sql = 'WHERE ' + ' AND '.join(where_clauses)
# 查询数据(不分页,全量导出)
sql = f'''
SELECT id, report_date, config_id, config_name, split_type, split_value, split_name,
sum_field_key, sum_field_name, sum_value, total_orders, sort_order, data_source, remark,
create_time, update_time
FROM t_daily_report_sum_data
{where_sql}
ORDER BY report_date DESC, sort_order ASC, config_id ASC, sum_field_key ASC
'''
rows = execute_query(sql, tuple(params) if params else ())
# 创建Excel
wb = Workbook()
ws = wb.active
ws.title = '求和数据'
# 表头
headers = ['序号', '报表日期', '配置名称', '拆分方式', '拆分值', '求和字段', '求和值', '订单数', '数据来源', '修改原因/备注', '创建时间', '更新时间']
header_fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid')
header_font = Font(bold=True, color='FFFFFF', size=11)
header_alignment = Alignment(horizontal='center', vertical='center')
thin_border = Border(
left=Side(style='thin', color='000000'),
right=Side(style='thin', color='000000'),
top=Side(style='thin', color='000000'),
bottom=Side(style='thin', color='000000')
)
even_fill = PatternFill(start_color='F2F2F2', end_color='F2F2F2', fill_type='solid')
# 写入表头
for col_idx, header in enumerate(headers, 1):
cell = ws.cell(row=1, column=col_idx)
cell.value = header
cell.font = header_font
cell.fill = header_fill
cell.alignment = header_alignment
cell.border = thin_border
ws.row_dimensions[1].height = 25
# 拆分方式映射
split_type_map = {
'company_id': '按企业ID',
'user_id': '按用户ID',
'station_id': '按场站ID'
}
# 数据来源映射
source_map = {
'auto': '自动采集',
'manual': '手动编辑'
}
# 写入数据
for row_idx, row in enumerate(rows, 2):
is_even = (row_idx - 2) % 2 == 1
values = [
row.get('sort_order') or '',
row.get('report_date', ''),
row.get('config_name', ''),
split_type_map.get(row.get('split_type', ''), row.get('split_type', '')),
row.get('split_name', '') or row.get('split_value', ''),
row.get('sum_field_name', ''),
float(row.get('sum_value', 0) or 0),
int(row.get('total_orders', 0) or 0),
source_map.get(row.get('data_source', ''), row.get('data_source', '')),
row.get('remark', ''),
str(row.get('create_time', '')) if row.get('create_time') else '',
str(row.get('update_time', '')) if row.get('update_time') else ''
]
for col_idx, val in enumerate(values, 1):
cell = ws.cell(row=row_idx, column=col_idx)
cell.value = val
cell.border = thin_border
# 对齐方式:数字靠右,其他居中或靠左
if col_idx in [7, 8]: # 求和值、订单数
cell.alignment = Alignment(horizontal='right', vertical='center')
if col_idx == 7:
cell.number_format = '0.000'
elif col_idx in [1, 2, 4, 9]: # 序号、日期、拆分方式、数据来源
cell.alignment = Alignment(horizontal='center', vertical='center')
else:
cell.alignment = Alignment(horizontal='left', vertical='center')
if is_even:
cell.fill = even_fill
ws.row_dimensions[row_idx].height = 22
# 调整列宽
col_widths = [8, 14, 20, 12, 20, 18, 14, 10, 12, 30, 20, 20]
for col_idx, width in enumerate(col_widths, 1):
from openpyxl.utils import get_column_letter
col_letter = get_column_letter(col_idx)
ws.column_dimensions[col_letter].width = width
# 冻结首行
ws.freeze_panes = 'A2'
# 保存文件
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'求和数据导出_{timestamp}.xlsx'
file_path = os.path.join(reports_dir, filename)
wb.save(file_path)
return {'success': True, 'message': '导出成功', 'file_path': f'/public/reports/{filename}', 'filename': filename, 'total': len(rows)}
except Exception as e:
import traceback
traceback.print_exc()
return {'success': False, 'message': str(e)}
def get_monthly_charge_degree_total(config_id=None, month=None):
"""
获取当月充电电量累计(从求和数据表查询,对账后的准确数据)
注意:
- 数据来源于 t_daily_report_sum_data 表(线下对账后的数据,可能被手动修改过)
- 只统计 sum_field_key = 'charge_degree' 的记录
- 如果当天已经生成了日报并采集,自动包含在内,不会重复计算
Args: