115 lines
3.7 KiB
Python
115 lines
3.7 KiB
Python
import base64
|
||||
|
|
from datetime import datetime, timezone
|
|||
|
|
import math
|
|||
|
|
from zoneinfo import ZoneInfo
|
|||
|
|
from flask import current_app, json
|
|||
|
|
import numpy as np
|
|||
|
|
from werkzeug.security import generate_password_hash, check_password_hash
|
|||
|
|
import threading # 用于线程安全
|
|||
|
|
from Crypto.Cipher import AES
|
|||
|
|
from Crypto.Util.Padding import unpad
|
|||
|
|
|
|||
|
|
|
|||
|
|
# --- 全局状态管理 ---
|
|||
|
|
# 使用一个简单的标志和锁来确保线程安全
|
|||
|
|
cluster_ensured_running = False
|
|||
|
|
cluster_check_lock = threading.Lock()
|
|||
|
|
|
|||
|
|
|
|||
|
|
def encrypt_password(raw_password: str) -> str:
|
|||
|
|
"""加密密码(使用pbkdf2:sha256算法)"""
|
|||
|
|
return generate_password_hash(raw_password, method="pbkdf2:sha256", salt_length=16)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def verify_password(encrypted_password: str, raw_password: str) -> bool:
|
|||
|
|
"""验证密码"""
|
|||
|
|
return check_password_hash(encrypted_password, raw_password)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def convert_incident_time_format(data_list):
|
|||
|
|
for item in data_list:
|
|||
|
|
# 原始数据是Timestamp类型,直接获取
|
|||
|
|
original_ts = item["incident_time"]
|
|||
|
|
print('original_ts',type(original_ts))
|
|||
|
|
# 1. 为Timestamp添加原时区(假设原时间是Asia/Shanghai时区,且原始Timestamp无时区)
|
|||
|
|
# 注意:如果Timestamp已有时区,直接跳过localize,用tz_convert即可
|
|||
|
|
if original_ts.tz is None: # 检查是否已有时区
|
|||
|
|
original_tz_ts = original_ts.tz_localize(ZoneInfo("Asia/Shanghai"))
|
|||
|
|
else:
|
|||
|
|
original_tz_ts = original_ts # 已有时区则直接使用
|
|||
|
|
|
|||
|
|
# 2. 转换为UTC时区
|
|||
|
|
utc_ts = original_tz_ts.tz_convert(ZoneInfo("UTC"))
|
|||
|
|
|
|||
|
|
# 3. 格式化输出目标格式(添加T、毫秒和时区标识)
|
|||
|
|
target_str = utc_ts.strftime("%Y-%m-%dT%H:%M:%S") + ".000+0000"
|
|||
|
|
|
|||
|
|
# 更新字段
|
|||
|
|
item["incident_time"] = target_str
|
|||
|
|
|
|||
|
|
|
|||
|
|
# 处理NaN的值
|
|||
|
|
# processed_data = {}
|
|||
|
|
for key, value in item.items():
|
|||
|
|
# 检查当前值是否为NaN(仅针对数值类型)
|
|||
|
|
if isinstance(value, (int, float)): # 确保是数值类型
|
|||
|
|
# 判断是否为NaN(同时兼容math.nan和np.nan)
|
|||
|
|
if math.isnan(value) or np.isnan(value):
|
|||
|
|
item[key] = None
|
|||
|
|
else:
|
|||
|
|
item[key] = value
|
|||
|
|
else:
|
|||
|
|
# 非数值类型(如字符串、None等)直接保留
|
|||
|
|
item[key] = value
|
|||
|
|
|
|||
|
|
return data_list
|
|||
|
|
|
|||
|
|
# gen5的车码查询前替换为真实的车码
|
|||
|
|
def mask_vin(real_vin):
|
|||
|
|
id_str = real_vin[-6:]
|
|||
|
|
if not id_str.isdigit():
|
|||
|
|
print('The last six digits of vin:%s are not numbers!' % real_vin)
|
|||
|
|
return real_vin
|
|||
|
|
masked_id = ''
|
|||
|
|
add = 0
|
|||
|
|
total = 0
|
|||
|
|
mapping = {
|
|||
|
|
73: 88,
|
|||
|
|
79: 89,
|
|||
|
|
81: 90,
|
|||
|
|
}
|
|||
|
|
for i in id_str:
|
|||
|
|
total += int(i)
|
|||
|
|
for i in id_str:
|
|||
|
|
add += total + 2
|
|||
|
|
c = int(i) + 65 + add % 13
|
|||
|
|
if c in [73, 79, 81]:
|
|||
|
|
c = mapping[c]
|
|||
|
|
masked_id += chr(c)
|
|||
|
|
|
|||
|
|
masked_vin = real_vin[:-6] + masked_id[::-1]
|
|||
|
|
|
|||
|
|
return masked_vin
|
|||
|
|
|
|||
|
|
|
|||
|
|
def decrypt_code(encrypted_data):
|
|||
|
|
try:
|
|||
|
|
raw_key=current_app.config['HEADERS_SECRET_KEY']
|
|||
|
|
key_bytes=raw_key.encode('utf-8')[:16]
|
|||
|
|
if len(key_bytes)<16:
|
|||
|
|
key_bytes=key_bytes.ljust(16,b"0")
|
|||
|
|
|
|||
|
|
combined=base64.b64decode(encrypted_data)
|
|||
|
|
iv_byte=combined[:16]
|
|||
|
|
ct_byte=combined[16:]
|
|||
|
|
|
|||
|
|
ciper=AES.new(key_bytes,AES.MODE_CBC,iv=iv_byte)
|
|||
|
|
des_padd=ciper.decrypt(ct_byte)
|
|||
|
|
|
|||
|
|
desrypt_res=unpad(des_padd,AES.block_size)
|
|||
|
|
|
|||
|
|
return desrypt_res.decode('utf-8')
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
print('解析失败')
|
|||
|
|
return None
|