时间显示异常

This commit is contained in:
YueL1331
2026-01-14 14:42:33 +08:00
parent 9ebfd79414
commit f043983d24
4 changed files with 137 additions and 48 deletions

View File

@ -11,7 +11,7 @@ from flask_apscheduler import APScheduler
# ✅ 1. 核心模块引用
# ==============================================================================
try:
# [新增] 导入配置类
# 导入配置类
from config import Config
# 数据库实例
@ -23,13 +23,13 @@ try:
# 核心业务逻辑 (爬虫)
from services.core import execute_monitor_task
# [新增] 核心业务逻辑 (IoT) - 用于定时任务
# 核心业务逻辑 (IoT) - 用于定时任务
from services.iot_api import sync_iot_data_service
# 路由蓝图
try:
from routes.api import api_bp as device_bp
# [新增] 导入保存逻辑,供定时任务复用
# 导入保存逻辑,供定时任务复用
from routes.api import save_iot_cards_to_db, calculate_offset
except ImportError:
from routes.api import device_bp, save_iot_cards_to_db, calculate_offset
@ -40,22 +40,39 @@ except ImportError as e:
# ==============================================================================
# 2. 路径计算 (辅助静态文件服务)
# 2. 路径计算 (核心修复:区分资源路径和数据路径)
# ==============================================================================
# 注意Config 类中已经处理了数据库路径,这里主要处理 web_dist 静态资源路径
def get_base_path():
def get_paths():
"""
计算关键路径:
1. resource_base: 用于存放 web_dist (打包后在临时目录)
2. data_base: 用于存放数据库 (打包后在 exe 旁边)
"""
if getattr(sys, 'frozen', False):
if hasattr(sys, '_MEIPASS'):
return sys._MEIPASS
else:
return os.path.dirname(os.path.abspath(sys.executable))
# --- 打包环境 (PyInstaller) ---
# 资源文件在临时解压目录 (sys._MEIPASS)
resource_base = sys._MEIPASS
# 数据文件(数据库)在 exe 所在目录 (sys.executable 的父目录)
data_base = os.path.dirname(sys.executable)
else:
return os.path.abspath(os.path.dirname(__file__))
# --- 开发环境 ---
base = os.path.abspath(os.path.dirname(__file__))
resource_base = base
data_base = base
return resource_base, data_base
BASE_DIR = get_base_path()
STATIC_FOLDER = os.path.join(BASE_DIR, 'web_dist')
INSTANCE_FOLDER = os.path.join(BASE_DIR, 'instance')
# 获取路径
RESOURCE_BASE, DATA_BASE = get_paths()
# 定义具体文件夹路径
STATIC_FOLDER = os.path.join(RESOURCE_BASE, 'web_dist')
# ⚠️ 关键:强制将 instance 文件夹定位到数据目录 (exe旁边),而不是临时目录
INSTANCE_PATH = os.path.join(DATA_BASE, 'instance')
# 修复 Windows MIME 类型
mimetypes.add_type('application/javascript', '.js')
@ -63,7 +80,7 @@ mimetypes.add_type('text/css', '.css')
# ==============================================================================
# 3. 定时任务逻辑 (同时运行 爬虫 + IoT同步)
# 3. 定时任务逻辑 (保持不变)
# ==============================================================================
def auto_monitor_job(app):
"""定时任务具体执行逻辑"""
@ -109,7 +126,7 @@ def auto_monitor_job(app):
except Exception as e:
print(f"❌ [定时任务-爬虫] 异常: {e}")
# --- 任务 B: IoT 同步 (新增) ---
# --- 任务 B: IoT 同步 ---
if sync_iot_data_service:
try:
# 1. 获取数据
@ -136,21 +153,35 @@ def auto_monitor_job(app):
# 4. Flask 应用工厂
# ==============================================================================
def create_app():
# 指定静态文件
app = Flask(__name__, static_folder=STATIC_FOLDER)
# ⚠️ 关键修改:显式传入 instance_path告诉 Flask 去哪里找/存 数据库文件
app = Flask(__name__, static_folder=STATIC_FOLDER, instance_path=INSTANCE_PATH)
CORS(app)
# 1. 确保 instance 目录存在
if not os.path.exists(INSTANCE_FOLDER):
os.makedirs(INSTANCE_FOLDER, exist_ok=True)
# 1. 确保 instance 目录存在 (在 exe 旁边创建文件夹)
if not os.path.exists(app.instance_path):
try:
os.makedirs(app.instance_path, exist_ok=True)
print(f"📁 已创建数据目录: {app.instance_path}")
except OSError as e:
print(f"❌ 无法创建数据目录 {app.instance_path}: {e}")
# ==========================================================
# ✅ 2. 核心修复:加载 config.py 中的配置
# ==========================================================
# 2. 加载配置
app.config.from_object(Config)
# 打印一下关键配置,确保 IoT 配置已加载 (调试用)
# print(f"DEBUG Config Loaded: IOT_APP_ID={app.config.get('IOT_APP_ID')}")
# ⚠️ 关键修改:强制重写数据库 URI确保使用绝对路径
# 即使 Config 里写了,这里也要确保它指向我们刚才计算出的 INSTANCE_PATH
db_name = 'monitor_data.db' # 你的数据库文件名
db_path = os.path.join(app.instance_path, db_name)
# Windows下路径分隔符处理防止报错
if sys.platform.startswith('win'):
app.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite:///{db_path}'
else:
app.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite:////{db_path}'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
print(f"💾 数据库路径锁定为: {db_path}")
# 3. 初始化扩展
db.init_app(app)
@ -177,8 +208,9 @@ def create_app():
# -------------------------------------------------
@app.route('/')
def serve_index():
if not os.path.exists(os.path.join(app.static_folder, 'index.html')):
return "❌ 错误: 前端文件丢失 (web_dist/index.html)", 404
index_path = os.path.join(app.static_folder, 'index.html')
if not os.path.exists(index_path):
return f"❌ 错误: 前端文件丢失 ({index_path})", 404
return send_from_directory(app.static_folder, 'index.html')
@app.route('/<path:path>')
@ -193,6 +225,7 @@ def create_app():
return send_from_directory(app.static_folder, 'index.html')
with app.app_context():
# 自动创建表结构
db.create_all()
return app
@ -202,5 +235,5 @@ if __name__ == '__main__':
app = create_app()
debug_mode = not getattr(sys, 'frozen', False)
print("🚀 服务启动中...")
# 注意:use_reloader=False 防止定时任务执行两次
# use_reloader=False 防止定时任务执行两次
app.run(host='0.0.0.0', port=5000, debug=debug_mode, use_reloader=False)

View File

@ -258,6 +258,25 @@ def devices_overview():
for d in devices:
item = d.to_dict()
# =========== 【新增修复】强制格式化时间 ===========
# 无论模型返回什么,这里都强制从数据库原始字段获取,并确保包含时分秒
raw_time = d.latest_time
if raw_time:
# 1. 如果是 datetime 对象 (防万一)
if hasattr(raw_time, 'strftime'):
item['latest_time'] = raw_time.strftime("%Y-%m-%d %H:%M:%S")
# 2. 如果是字符串
else:
s = str(raw_time).strip()
# 如果只有日期且用下划线 (如 2026_01_14),且没有冒号,则补全
if '_' in s and ':' not in s:
item['latest_time'] = s.replace('_', '-') + " 00:00:00"
else:
# 已经是正常字符串(如 2026-01-14 13:49:26直接使用
item['latest_time'] = s
# ===============================================
parsed_content = {}
if d.json_data:
try: