422 lines
17 KiB
Python
422 lines
17 KiB
Python
"""
|
|
配置管理 API
|
|
"""
|
|
import json
|
|
from datetime import datetime
|
|
from flask import request, jsonify
|
|
from lib.db import execute_query, execute_update, execute_insert
|
|
from . import config_bp
|
|
|
|
|
|
@config_bp.route('/config', methods=['GET'])
|
|
def get_configs():
|
|
"""获取所有配置"""
|
|
try:
|
|
configs = execute_query(
|
|
'SELECT * FROM t_daily_report_config ORDER BY sort_order ASC, id DESC'
|
|
)
|
|
|
|
# 解析 JSON 字段
|
|
for config in configs:
|
|
config['selected_fields'] = json.loads(config['selected_fields'])
|
|
config['sum_fields'] = json.loads(config['sum_fields']) if config['sum_fields'] else []
|
|
config['time_periods'] = json.loads(config['time_periods']) if config.get('time_periods') else {}
|
|
|
|
return jsonify({'success': True, 'data': configs})
|
|
except Exception as e:
|
|
return jsonify({'success': False, 'message': str(e)}), 500
|
|
|
|
|
|
@config_bp.route('/config', methods=['POST'])
|
|
def create_config():
|
|
"""创建配置"""
|
|
try:
|
|
data = request.json
|
|
config_name = data.get('config_name')
|
|
split_type = data.get('split_type')
|
|
split_value = data.get('split_value')
|
|
split_name = data.get('split_name', '')
|
|
selected_fields = data.get('selected_fields', [])
|
|
sum_fields = data.get('sum_fields', [])
|
|
time_periods = data.get('time_periods', {})
|
|
merge_telecom = 1 if data.get('merge_telecom') else 0
|
|
telecom_vehicle_no = data.get('telecom_vehicle_no', '')
|
|
field_custom_names = data.get('field_custom_names', {})
|
|
show_monthly_total = 1 if data.get('show_monthly_total') else 0
|
|
|
|
if not config_name or not split_type or not split_value:
|
|
return jsonify({'success': False, 'message': '缺少必填字段'}), 400
|
|
|
|
if not selected_fields:
|
|
return jsonify({'success': False, 'message': '请至少选择一个字段'}), 400
|
|
|
|
# 生成 ID
|
|
config_id = int(datetime.now().timestamp() * 1000)
|
|
|
|
# 获取最大排序值
|
|
max_sort = execute_query('SELECT MAX(sort_order) as max_sort FROM t_daily_report_config')
|
|
sort_order = (max_sort[0]['max_sort'] or 0) + 1
|
|
|
|
sql = """
|
|
INSERT INTO t_daily_report_config
|
|
(id, config_name, split_type, split_value, split_name, selected_fields,
|
|
sum_fields, time_periods, merge_telecom, telecom_vehicle_no, field_custom_names,
|
|
show_monthly_total, is_active, sort_order, create_time, update_time)
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, 1, %s, NOW(), NOW())
|
|
"""
|
|
|
|
params = (
|
|
config_id,
|
|
config_name,
|
|
split_type,
|
|
split_value,
|
|
split_name,
|
|
json.dumps(selected_fields, ensure_ascii=False),
|
|
json.dumps(sum_fields, ensure_ascii=False),
|
|
json.dumps(time_periods, ensure_ascii=False),
|
|
merge_telecom,
|
|
telecom_vehicle_no,
|
|
json.dumps(field_custom_names, ensure_ascii=False) if field_custom_names else None,
|
|
show_monthly_total,
|
|
sort_order
|
|
)
|
|
|
|
execute_insert(sql, params)
|
|
|
|
return jsonify({
|
|
'success': True,
|
|
'message': '配置创建成功',
|
|
'data': {'id': config_id}
|
|
})
|
|
except Exception as e:
|
|
return jsonify({'success': False, 'message': str(e)}), 500
|
|
|
|
|
|
@config_bp.route('/config/<int:config_id>', methods=['PUT'])
|
|
def update_config(config_id):
|
|
"""更新配置"""
|
|
try:
|
|
data = request.json
|
|
config_name = data.get('config_name')
|
|
split_type = data.get('split_type')
|
|
split_value = data.get('split_value')
|
|
split_name = data.get('split_name', '')
|
|
selected_fields = data.get('selected_fields', [])
|
|
sum_fields = data.get('sum_fields', [])
|
|
time_periods = data.get('time_periods', {})
|
|
merge_telecom = 1 if data.get('merge_telecom') else 0
|
|
telecom_vehicle_no = data.get('telecom_vehicle_no', '')
|
|
field_custom_names = data.get('field_custom_names', {})
|
|
show_monthly_total = 1 if data.get('show_monthly_total') else 0
|
|
|
|
sql = """
|
|
UPDATE t_daily_report_config
|
|
SET config_name = %s, split_type = %s, split_value = %s, split_name = %s,
|
|
selected_fields = %s, sum_fields = %s, time_periods = %s,
|
|
merge_telecom = %s, telecom_vehicle_no = %s, field_custom_names = %s,
|
|
show_monthly_total = %s, update_time = NOW()
|
|
WHERE id = %s
|
|
"""
|
|
|
|
params = (
|
|
config_name,
|
|
split_type,
|
|
split_value,
|
|
split_name,
|
|
json.dumps(selected_fields, ensure_ascii=False),
|
|
json.dumps(sum_fields, ensure_ascii=False),
|
|
json.dumps(time_periods, ensure_ascii=False),
|
|
merge_telecom,
|
|
telecom_vehicle_no,
|
|
json.dumps(field_custom_names, ensure_ascii=False) if field_custom_names else None,
|
|
show_monthly_total,
|
|
config_id
|
|
)
|
|
|
|
execute_update(sql, params)
|
|
|
|
return jsonify({'success': True, 'message': '配置更新成功'})
|
|
except Exception as e:
|
|
return jsonify({'success': False, 'message': str(e)}), 500
|
|
|
|
|
|
@config_bp.route('/config/<int:config_id>', methods=['DELETE'])
|
|
def delete_config(config_id):
|
|
"""删除配置"""
|
|
try:
|
|
execute_update('DELETE FROM t_daily_report_config WHERE id = %s', (config_id,))
|
|
return jsonify({'success': True, 'message': '配置删除成功'})
|
|
except Exception as e:
|
|
return jsonify({'success': False, 'message': str(e)}), 500
|
|
|
|
|
|
@config_bp.route('/config/<int:config_id>/copy', methods=['POST'])
|
|
def copy_config(config_id):
|
|
"""复制配置"""
|
|
try:
|
|
# 获取原配置
|
|
configs = execute_query('SELECT * FROM t_daily_report_config WHERE id = %s', (config_id,))
|
|
if not configs:
|
|
return jsonify({'success': False, 'message': '原配置不存在'}), 404
|
|
|
|
original = configs[0]
|
|
|
|
# 生成新 ID 和新名称
|
|
new_id = int(datetime.now().timestamp() * 1000)
|
|
new_name = f"{original['config_name']}_副本"
|
|
|
|
# 获取最大排序值
|
|
max_sort = execute_query('SELECT MAX(sort_order) as max_sort FROM t_daily_report_config')
|
|
sort_order = (max_sort[0]['max_sort'] or 0) + 1
|
|
|
|
# 插入新配置
|
|
execute_insert(
|
|
'''INSERT INTO t_daily_report_config
|
|
(id, config_name, split_type, split_value, split_name, selected_fields, sum_fields, time_periods, merge_telecom, telecom_vehicle_no, field_custom_names, show_monthly_total, is_active, sort_order, create_time, update_time)
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, NOW(), NOW())''',
|
|
(
|
|
new_id,
|
|
new_name,
|
|
original['split_type'],
|
|
original['split_value'],
|
|
original['split_name'],
|
|
original['selected_fields'],
|
|
original.get('sum_fields', ''),
|
|
original.get('time_periods', ''),
|
|
original.get('merge_telecom', 0),
|
|
original.get('telecom_vehicle_no', ''),
|
|
original.get('field_custom_names', None),
|
|
original.get('show_monthly_total', 0),
|
|
original['is_active'],
|
|
sort_order
|
|
)
|
|
)
|
|
|
|
return jsonify({'success': True, 'message': '配置复制成功', 'id': new_id})
|
|
except Exception as e:
|
|
return jsonify({'success': False, 'message': str(e)}), 500
|
|
|
|
|
|
@config_bp.route('/config/<int:config_id>/toggle', methods=['POST'])
|
|
def toggle_config(config_id):
|
|
"""启用/禁用配置"""
|
|
try:
|
|
execute_update(
|
|
'UPDATE t_daily_report_config SET is_active = NOT is_active, update_time = NOW() WHERE id = %s',
|
|
(config_id,)
|
|
)
|
|
return jsonify({'success': True, 'message': '操作成功'})
|
|
except Exception as e:
|
|
return jsonify({'success': False, 'message': str(e)}), 500
|
|
|
|
|
|
@config_bp.route('/config/<int:config_id>/move', methods=['POST'])
|
|
def move_config(config_id):
|
|
"""移动配置排序"""
|
|
try:
|
|
direction = request.json.get('direction') # up 或 down
|
|
print(f"[move_config] 开始移动 config_id={config_id}, direction={direction}")
|
|
|
|
# 查出所有配置,按 sort_order, id 排序
|
|
all_configs = execute_query(
|
|
'SELECT id, sort_order FROM t_daily_report_config ORDER BY sort_order ASC, id ASC'
|
|
)
|
|
print(f"[move_config] 所有配置数量: {len(all_configs)}")
|
|
for idx, c in enumerate(all_configs):
|
|
print(f" [{idx}] id={c['id']}, sort_order={c['sort_order']}")
|
|
|
|
# 找到当前配置的索引
|
|
current_index = None
|
|
for i, c in enumerate(all_configs):
|
|
if c['id'] == config_id:
|
|
current_index = i
|
|
break
|
|
|
|
if current_index is None:
|
|
print(f"[move_config] 未找到配置 id={config_id}")
|
|
return jsonify({'success': False, 'message': '配置不存在'}), 404
|
|
|
|
print(f"[move_config] 当前配置索引: {current_index}")
|
|
|
|
# 计算目标索引
|
|
if direction == 'up':
|
|
target_index = current_index - 1
|
|
elif direction == 'down':
|
|
target_index = current_index + 1
|
|
else:
|
|
return jsonify({'success': False, 'message': '无效的移动方向'}), 400
|
|
|
|
# 边界检查
|
|
if target_index < 0 or target_index >= len(all_configs):
|
|
print(f"[move_config] 已到边界,无法移动 target_index={target_index}")
|
|
return jsonify({'success': True, 'message': '已到边界'})
|
|
|
|
# 在内存数组中交换位置
|
|
all_configs[current_index], all_configs[target_index] = all_configs[target_index], all_configs[current_index]
|
|
|
|
print(f"[move_config] 交换后顺序: {[c['id'] for c in all_configs]}")
|
|
|
|
# 全量重排:按新顺序给每个配置重新赋值 sort_order
|
|
for idx, cfg in enumerate(all_configs):
|
|
new_sort = idx + 1
|
|
print(f" 更新 id={cfg['id']}: sort_order {cfg['sort_order']} -> {new_sort}")
|
|
execute_update(
|
|
'UPDATE t_daily_report_config SET sort_order = %s WHERE id = %s',
|
|
(new_sort, cfg['id'])
|
|
)
|
|
|
|
print("[move_config] 重排完成")
|
|
return jsonify({'success': True, 'message': '移动成功'})
|
|
except Exception as e:
|
|
import traceback
|
|
traceback.print_exc()
|
|
print(f"[move_config] 异常: {e}")
|
|
return jsonify({'success': False, 'message': str(e)}), 500
|
|
|
|
|
|
@config_bp.route('/config/export', methods=['GET'])
|
|
def export_configs():
|
|
"""导出所有配置为JSON文件"""
|
|
try:
|
|
from flask import Response
|
|
import os
|
|
|
|
configs = execute_query(
|
|
'SELECT * FROM t_daily_report_config ORDER BY sort_order ASC, id ASC'
|
|
)
|
|
|
|
export_list = []
|
|
for config in configs:
|
|
export_item = {}
|
|
for key, value in config.items():
|
|
if key == 'id':
|
|
continue
|
|
export_item[key] = value
|
|
export_list.append(export_item)
|
|
|
|
export_data = {
|
|
'version': '1.0',
|
|
'export_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
|
'total': len(export_list),
|
|
'configs': export_list
|
|
}
|
|
|
|
json_str = json.dumps(export_data, ensure_ascii=False, indent=2)
|
|
filename = f'日报配置导出_{datetime.now().strftime("%Y%m%d_%H%M%S")}.json'
|
|
|
|
response = Response(json_str, mimetype='application/json')
|
|
response.headers['Content-Disposition'] = f'attachment; filename="{filename}"'
|
|
return response
|
|
except Exception as e:
|
|
import traceback
|
|
traceback.print_exc()
|
|
return jsonify({'success': False, 'message': f'导出失败: {str(e)}'}), 500
|
|
|
|
|
|
@config_bp.route('/config/import', methods=['POST'])
|
|
def import_configs():
|
|
"""从JSON文件导入配置"""
|
|
try:
|
|
if 'file' not in request.files:
|
|
return jsonify({'success': False, 'message': '请选择要导入的文件'}), 400
|
|
|
|
file = request.files['file']
|
|
if file.filename == '':
|
|
return jsonify({'success': False, 'message': '请选择要导入的文件'}), 400
|
|
|
|
if not file.filename.endswith('.json'):
|
|
return jsonify({'success': False, 'message': '只支持 JSON 格式的配置文件'}), 400
|
|
|
|
file_content = file.read().decode('utf-8')
|
|
import_data = json.loads(file_content)
|
|
|
|
if 'configs' not in import_data:
|
|
return jsonify({'success': False, 'message': '文件格式不正确,缺少 configs 字段'}), 400
|
|
|
|
configs_to_import = import_data['configs']
|
|
if not isinstance(configs_to_import, list) or len(configs_to_import) == 0:
|
|
return jsonify({'success': False, 'message': '没有可导入的配置'}), 400
|
|
|
|
max_sort_result = execute_query('SELECT MAX(sort_order) as max_sort FROM t_daily_report_config')
|
|
current_max_sort = max_sort_result[0]['max_sort'] or 0
|
|
|
|
success_count = 0
|
|
fail_count = 0
|
|
fail_messages = []
|
|
|
|
for idx, config_data in enumerate(configs_to_import):
|
|
try:
|
|
config_name = config_data.get('config_name', '')
|
|
split_type = config_data.get('split_type', '')
|
|
split_value = config_data.get('split_value', '')
|
|
|
|
if not config_name or not split_type or not split_value:
|
|
fail_count += 1
|
|
fail_messages.append(f'第{idx+1}条:缺少必填字段')
|
|
continue
|
|
|
|
new_id = int(datetime.now().timestamp() * 1000) + idx
|
|
new_name = f"{config_name}_导入"
|
|
sort_order = current_max_sort + 1
|
|
current_max_sort += 1
|
|
|
|
selected_fields = config_data.get('selected_fields', '[]')
|
|
if isinstance(selected_fields, str):
|
|
selected_fields = json.loads(selected_fields) if selected_fields else []
|
|
|
|
sum_fields = config_data.get('sum_fields', '[]')
|
|
if isinstance(sum_fields, str):
|
|
sum_fields = json.loads(sum_fields) if sum_fields else []
|
|
|
|
time_periods = config_data.get('time_periods', '{}')
|
|
if isinstance(time_periods, str):
|
|
time_periods = json.loads(time_periods) if time_periods else {}
|
|
|
|
field_custom_names = config_data.get('field_custom_names')
|
|
if isinstance(field_custom_names, str):
|
|
field_custom_names = json.loads(field_custom_names) if field_custom_names else None
|
|
|
|
sql = """
|
|
INSERT INTO t_daily_report_config
|
|
(id, config_name, split_type, split_value, split_name, selected_fields,
|
|
sum_fields, time_periods, merge_telecom, telecom_vehicle_no, field_custom_names,
|
|
show_monthly_total, is_active, sort_order, create_time, update_time)
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, 0, %s, NOW(), NOW())
|
|
"""
|
|
|
|
params = (
|
|
new_id,
|
|
new_name,
|
|
split_type,
|
|
split_value,
|
|
config_data.get('split_name', ''),
|
|
json.dumps(selected_fields, ensure_ascii=False),
|
|
json.dumps(sum_fields, ensure_ascii=False),
|
|
json.dumps(time_periods, ensure_ascii=False),
|
|
config_data.get('merge_telecom', 0),
|
|
config_data.get('telecom_vehicle_no', ''),
|
|
json.dumps(field_custom_names, ensure_ascii=False) if field_custom_names else None,
|
|
config_data.get('show_monthly_total', 0),
|
|
sort_order
|
|
)
|
|
|
|
execute_insert(sql, params)
|
|
success_count += 1
|
|
except Exception as item_error:
|
|
fail_count += 1
|
|
fail_messages.append(f'第{idx+1}条:{str(item_error)}')
|
|
|
|
return jsonify({
|
|
'success': True,
|
|
'message': f'导入完成:成功 {success_count} 条,失败 {fail_count} 条',
|
|
'data': {
|
|
'success_count': success_count,
|
|
'fail_count': fail_count,
|
|
'fail_messages': fail_messages
|
|
}
|
|
})
|
|
except Exception as e:
|
|
import traceback
|
|
traceback.print_exc()
|
|
return jsonify({'success': False, 'message': f'导入失败: {str(e)}'}), 500
|