# models.py from datetime import datetime from extensions import db # services.time_utils 只依赖 re/datetime,不会与 extensions/models 形成循环导入 from services.time_utils import staleness_of class Device(db.Model): __tablename__ = 'devices' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100), unique=True, index=True) source = db.Column(db.String(50)) # 快照字段(爬虫更新) status = db.Column(db.String(50)) current_value = db.Column(db.String(200)) latest_time = db.Column(db.String(50)) json_data = db.Column(db.Text) check_time = db.Column(db.String(50)) reason = db.Column(db.String(255)) offset = db.Column(db.String(50)) # ✅ 新增字段:文件数量 file_count = db.Column(db.Integer, default=0) # 手动录入字段(受保护,run_monitor 不主动覆盖) install_site = db.Column(db.String(100), default="") is_maintaining = db.Column(db.Boolean, default=False) is_hidden = db.Column(db.Boolean, default=False) # 白名单字段 (根据上下文可能存在,补全以防万一) is_whitelist = db.Column(db.Boolean, default=False) def to_dict(self): # 状态归一:中英文各种写法都要覆盖。 # 注意 'offline' 是 /add_device 创建手动设备时写入的值,原先的白名单 # 漏了它,导致手动添加的设备被映射成 online、在面板上永远显示“在线”。 raw_status = str(self.status or '').strip().lower() is_offline = raw_status in ( '离线', '异常', '已离线', 'offline', 'unknown', '未知' ) api_status = 'offline' if is_offline else 'online' # 数据时效在读取时实时计算,不读 offset 列。 # 该列是写入时刻的快照,采集一停摆就整体腐烂(库里停在 2026-02-06, # offset 却仍显示“当天”)。列保留但已废弃,见 services/db_ingest.py。 stale = staleness_of(self.latest_time) return { 'id': self.id, 'name': self.name, 'source': self.source, 'latest_time': self.latest_time, 'status': api_status, 'status_text': self.status, 'value': self.current_value, 'reason': self.reason, 'install_site': self.install_site or '', 'is_maintaining': self.is_maintaining, 'is_hidden': self.is_hidden, 'is_whitelist': self.is_whitelist, # 时效三件套:前端不再自己算滞后天数,统一消费这三个字段 'offset': stale['text'], # 中文描述,兼容旧调用方 'stale_level': stale['level'], # unknown/ok/yesterday/lagging/severe 'stale_days': stale['days'], # 滞后自然日数,unknown 时为 None 'file_count': self.file_count # ✅ 返回给前端 } class DeviceHistory(db.Model): __tablename__ = 'device_history' id = db.Column(db.Integer, primary_key=True) device_id = db.Column(db.Integer, db.ForeignKey('devices.id')) data_time = db.Column(db.String(50)) status = db.Column(db.String(50)) result_data = db.Column(db.String(200), default="") json_data = db.Column(db.Text) file_path = db.Column(db.String(255)) # ✅ 新增字段:历史记录文件数量 file_count = db.Column(db.Integer, default=0) recorded_at = db.Column(db.DateTime, default=datetime.now) class MaintenanceLog(db.Model): __tablename__ = 'maintenance_logs' id = db.Column(db.Integer, primary_key=True) device_name = db.Column(db.String(100), nullable=False) engineer = db.Column(db.String(50)) location = db.Column(db.String(100)) content = db.Column(db.Text) timestamp = db.Column(db.DateTime, default=datetime.now) def to_dict(self): return { 'id': self.id, 'device_name': self.device_name, 'engineer': self.engineer or '', 'location': self.location or '', 'content': self.content, 'timestamp': self.timestamp.strftime('%Y-%m-%d %H:%M:%S') }