This commit is contained in:
ZhuJW
2026-07-10 18:55:55 +08:00
commit fce40c7d6c
317 changed files with 170079 additions and 0 deletions
@@ -0,0 +1,333 @@
from datetime import datetime, timedelta, timezone
from queue import Queue
import threading
from flask import Blueprint, json, jsonify, current_app, request
from app.blueprints.incident_gen6.service import aggregate_and_sort_incidents_gen6, aggregate_line_list_gen6, convert_to_histro_data_v2, get_logging_list_gen6, process_incident_data_gen6
from app.link_wedata_utils import sync_query_tencent_cloud_data
from app.utils import decrypt_code
incident_gen6_bp = Blueprint("incident_gen6_bp", __name__)
@incident_gen6_bp.route("/list1", methods=["POST"])
def acc_list1():
encrypted = request.headers.get('X-Encrypted-Timestamp')
if not encrypted:
return jsonify({"msg": "Parameters are Incorrect"}), 400
encrypt_str=decrypt_code(encrypted)
if not encrypt_str:
return jsonify({"msg": "Invalid request"}), 400
data = request.get_json()
incident_type = data.get("incident_type", "").strip() or None
singal_name = data.get("singal_name", []) or None
# print("singal_name",singal_name)
import os
current_dir = os.path.dirname(os.path.abspath(__file__))
json_path = os.path.join(current_dir, "incident_sample.json")
with open(json_path, "r", encoding="utf-8") as f:
events_data = json.load(f)
histo_path = os.path.join(current_dir, "histro.json")
with open(histo_path, "r", encoding="utf-8") as f_histro:
histo_data = json.load(f_histro)
histo_data_new = []
for item in histo_data:
new_item = {
'signal': item['signal'],
'time': item['time'],
'value': item['value'],
'valueExplanation': item['valueExplanation'],
'pre_value':item['pre_value'],
'pre_valueExplanation':item['pre_valueExplanation'],
'is_pre_value_needed':item['is_pre_value_needed']
}
histo_data_new.append(new_item)
histo_list=convert_to_histro_data_v2(histo_data_new)
events_data_new=[]
for item in events_data:
new_item = {
'oneid': item['oneid'],
'incident_name': item['incident_name'],
'incident_time': item['incident_time'],
'key': item['key'],
'signal_value': item['signal_value'],
'latitude': item['latitude'],
'longitude': item['longitude'],
'odometer': item['odometer'],
'speed': item['speed']
}
events_data_new.append(new_item)
if incident_type == "Parking":
# 过滤出 incident_name 等于 Test_I_Park_Trip 的数据
events_data_new = [
item for item in events_data_new
if item.get("incident_name") in ["Test_I_Park_Trip", "Test_I_RMA"]
]
elif incident_type == "Driving":
# 过滤出 incident_name 不等于 Test_I_Park_Trip 的数据
events_data_new = [
item for item in events_data_new
if item.get("incident_name") not in ["Test_I_Park_Trip", "Test_I_RMA"]
]
# event数据
events_list=process_incident_data_gen6(events_data_new) # 初始的event list数据,但是没有15分钟处理
map_list = aggregate_and_sort_incidents_gen6(events_data_new)
# print(f"共 {len(map_list)} 条:")
line_list = aggregate_line_list_gen6(map_list)
logging_list=get_logging_list_gen6(map_list)
response = {
"code": 200,
"data": {
# "events_data":events_data,
"events_list": events_list,
"map_list": map_list,
"line_list": line_list,
"logging_list": logging_list,
"histo_list":histo_list
},
"msg": "success",
}
return jsonify(response)
@incident_gen6_bp.route("/list", methods=["POST"])
def acc_list():
# 验证请求头的信息,通过才继续
encrypted = request.headers.get('X-Encrypted-Timestamp')
if not encrypted:
return jsonify({"msg": "Parameters are Incorrect"}), 400
encrypt_str=decrypt_code(encrypted)
if not encrypt_str:
return jsonify({"msg": "Invalid request"}), 400
data = request.get_json()
incident_type = data.get("incident_type", "").strip() or None
signal_name = data.get("signal_name", [])
if not data:
return jsonify({"code": 400, "data": None, "msg": "请填写有效参数"}), 400
required_fields = ["incident_time", "oneid"]
for field in required_fields:
if field not in data:
return (
jsonify({"code": 400, "data": None, "msg": f"缺少必需字段: {field}"}),
400,
)
incident_time = data["incident_time"]
# 检查是否为非空列表
if not isinstance(incident_time, list) or len(incident_time) != 2:
return (
jsonify(
{
"code": 400,
"data": [],
"msg": "incident_time必须是包含两个时间的数组",
}
),
400,
)
oneid = data["oneid"]
if not isinstance(oneid, str) or not oneid.strip():
return jsonify({"code": 400, "data": None, "msg": "oneid必须是非空字符串"}), 400
config = {
'region': 'ap-shanghai', # 引擎所在地域
'secret_id': current_app.config['WEDATA_SECRET_ID'],
'secret_key': current_app.config['WEDATA_SECRET_KEY'],
'engine': current_app.config['WEDATA_ENGINE'], # 引擎名称
"database":current_app.config['WEDATA_DATABASE']
}
# 生成sql
try:
incid_sql, incid_params = generate_incidents_sql(incident_time, oneid, signal_name, table_name='gen6_incidents')
histo_sql, histo_params = generate_incidents_sql(incident_time, oneid, signal_name, table_name='gen6_histograms')
except ValueError as e:
return jsonify({"code": 400, "data": None, "msg": f"参数错误:{str(e)}"}), 400
print("incid_sql", incid_sql)
print("histo_sql", histo_sql)
result_queue = Queue(maxsize=2) # 最多存放2个结果
# ========== 仅修改这里:给原函数加极简异常防护(不新增任何函数) ==========
def safe_query(*args):
"""极简包装:仅捕获异常,保证队列必有结果"""
try:
# 直接调用原函数,参数完全传透(args就是config, sql, params, queue, task_id
sync_query_tencent_cloud_data(*args)
except Exception as e: # except Exception as e:
# 异常时手动向队列写错误结果(args[3]是queueargs[4]是task_id
args[3].put({"task_id": args[4], "success": False, "data": None, "error": str(e)})
# try:
# error_msg=(
# f'数据库连接失败:{str(e)}'
# if str(e)
# else f'底层系统错误:{type(e).__name__}'
# )
# args[3].put({"task_id": args[4], "success": False, "data": None, "error":error_msg})
# except Exception as query_err:
# print(f'任务失败,无法写入队列:{query_err},原始错误是:{e}')
# 5. 创建并启动两个查询线程
thread1 = threading.Thread(
target=safe_query,
args=(config, incid_sql, incid_params, result_queue, "incidents") # task_id 标记为 incidents
)
thread2 = threading.Thread(
target=safe_query,
args=(config, histo_sql, histo_params, result_queue, "histo") # task_id 标记为 signals
)
# 启动线程
thread1.start()
thread2.start()
thread1.join()
thread2.join()
# 从队列中提取结果
results_dict = {}
for _ in range(2):
task_result = result_queue.get()
results_dict[task_result["task_id"]] = task_result
# 8. 检查是否有查询失败
has_error = False
error_msg = ""
for task_id, res in results_dict.items():
if not res["success"]:
has_error = True
error_msg = f"查询异常:{res['error'][:80]}" # 错误信息保留前80
if has_error:
return jsonify({
"code": 500,
"msg": f"查询失败:{error_msg}",
"data": None
}), 500
# 9. 获取两个表的查询结果(业务处理核心)
events_data = results_dict["incidents"]["data"]
histo_data = results_dict["histo"]["data"]
histo_list=convert_to_histro_data_v2(histo_data)
if incident_type == "Parking":
# 过滤出 incident_name 等于 Test_I_Park_Trip 的数据
events_data = [
item for item in events_data
if item.get("incident_name") in ["Test_I_Park_Trip", "Test_I_RMA"]
]
elif incident_type == "Driving":
# 过滤出 incident_name 不等于 Test_I_Park_Trip 的数据
events_data = [
item for item in events_data
if item.get("incident_name") not in ["Test_I_Park_Trip", "Test_I_RMA"]
]
# event数据
events_list=process_incident_data_gen6(events_data) # 初始的event list数据,但是没有15分钟处理
map_list = aggregate_and_sort_incidents_gen6(events_data)
line_list = aggregate_line_list_gen6(map_list)
logging_list=get_logging_list_gen6(map_list)
response = {
"code": 200,
"data": {
# "events_data":events_data,
"events_list": events_list,
"map_list": [],
"line_list": line_list,
"logging_list": logging_list,
"histo_list":histo_list
},
"msg": "success",
}
return jsonify(response)
def generate_incidents_sql(incident_time: list, oneid: str, signal_list: list, table_name: str) -> tuple:
"""
简化版:仅处理用到的两个表,字段名映射极简,去掉冗余扩展
- gen6_incidents:时间=incident_timeID字段=oneid
- gen6_histograms:时间=timeID字段=vin
"""
# 处理UTC时间(带T/Z)转本地时间(东八区)
def convert_utc_to_cst(utc_time_str):
try:
# 解析UTC时间(支持带T/Z的格式)
utc_dt = datetime.fromisoformat(utc_time_str.replace('Z', '+00:00'))
# 转东八区(UTC+8
cst_tz = timezone(timedelta(hours=8))
cst_dt = utc_dt.astimezone(cst_tz)
# 转为数据库兼容的格式(YYYY-MM-DD HH:mm:ss.fff
return cst_dt.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3] # 保留3位毫秒
except Exception as e:
# 解析失败则返回原时间(兼容测试参数)
return utc_time_str
# 转换时间范围
incident_time = [convert_utc_to_cst(t) for t in incident_time]
# 1. 极简字段映射(只保留用到的表,避免KeyError)
FIELD_MAP = {
"gen6_incidents": {"time": "incident_time",
"id": "oneid",
# "select_fields": "oneid, incident_name,incident_time,`key`,signal_value,latitude,longitude,odometer,speed" },
"select_fields": "*" },
"gen6_histograms": {"time": "time",
"id": "vin",
"select_fields": "signal, time, value, valueExplanation"},
}
# 2. 基础校验(只校验用到的表,去掉冗余)
if table_name not in FIELD_MAP:
raise ValueError(f"仅支持查询表:{list(FIELD_MAP.keys())}")
if len(incident_time) != 2 or not all(incident_time):
raise ValueError("incident_time必须是包含两个非空时间的列表")
if not isinstance(oneid, str) or not oneid.strip():
raise ValueError("oneid必须是非空字符串")
# 3. 提取当前表的字段名(核心:只用两行完成字段替换)
time_field = FIELD_MAP[table_name]["time"]
id_field = FIELD_MAP[table_name]["id"]
select_fields = FIELD_MAP[table_name]["select_fields"]
# 4. 拼接SQL条件(极简逻辑,去掉冗余注释)
conditions = [
f"{time_field} BETWEEN %s AND %s",
f"{id_field} = %s"
]
params = [incident_time[0], incident_time[1], oneid]
# 仅gen6_histograms处理signal条件
if table_name == "gen6_histograms":
if signal_list:
signal_placeholders = ",".join(["%s"] * len(signal_list))
conditions.append(f"signal IN ({signal_placeholders})")
params += signal_list
else:
conditions.append("signal = %s")
params.append("DAS_DTR_UI_Stat_ST3")
# 5. 生成最终SQL(简化格式化)
sql = f"SELECT {select_fields} FROM {table_name} WHERE {' AND '.join(conditions)}"
return sql, params