fix: 统一数据库时间为北京时间(UTC+8)
- 新增 app/utils/timezone.py 提供 beijing_now() 工具函数 - models.py 所有表的 created_at/updated_at 默认值从 UTC 改为北京时间 - 解决 event_time(边缘端北京时间)与 handled_at(服务端UTC)差8小时的问题 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,8 @@
|
|||||||
import enum
|
import enum
|
||||||
import os
|
import os
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
|
from app.utils.timezone import beijing_now
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from sqlalchemy import (
|
from sqlalchemy import (
|
||||||
Column, String, Integer, SmallInteger, BigInteger, Boolean, Float, DateTime, Text, Enum, JSON,
|
Column, String, Integer, SmallInteger, BigInteger, Boolean, Float, DateTime, Text, Enum, JSON,
|
||||||
@@ -102,9 +104,9 @@ class Alert(Base):
|
|||||||
work_order_id = Column(Integer, ForeignKey("work_orders.id"), nullable=True)
|
work_order_id = Column(Integer, ForeignKey("work_orders.id"), nullable=True)
|
||||||
|
|
||||||
# 时间戳
|
# 时间戳
|
||||||
created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc))
|
created_at = Column(DateTime, default=lambda: beijing_now())
|
||||||
updated_at = Column(DateTime, default=lambda: datetime.now(timezone.utc),
|
updated_at = Column(DateTime, default=lambda: beijing_now(),
|
||||||
onupdate=lambda: datetime.now(timezone.utc))
|
onupdate=lambda: beijing_now())
|
||||||
|
|
||||||
# 关系
|
# 关系
|
||||||
work_order = relationship("WorkOrder", back_populates="alerts", foreign_keys=[work_order_id])
|
work_order = relationship("WorkOrder", back_populates="alerts", foreign_keys=[work_order_id])
|
||||||
@@ -175,9 +177,9 @@ class WorkOrder(Base):
|
|||||||
assigned_at = Column(DateTime, nullable=True)
|
assigned_at = Column(DateTime, nullable=True)
|
||||||
started_at = Column(DateTime, nullable=True)
|
started_at = Column(DateTime, nullable=True)
|
||||||
completed_at = Column(DateTime, nullable=True)
|
completed_at = Column(DateTime, nullable=True)
|
||||||
created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc))
|
created_at = Column(DateTime, default=lambda: beijing_now())
|
||||||
updated_at = Column(DateTime, default=lambda: datetime.now(timezone.utc),
|
updated_at = Column(DateTime, default=lambda: beijing_now(),
|
||||||
onupdate=lambda: datetime.now(timezone.utc))
|
onupdate=lambda: beijing_now())
|
||||||
|
|
||||||
# 关系
|
# 关系
|
||||||
alerts = relationship("Alert", back_populates="work_order", foreign_keys=[Alert.work_order_id])
|
alerts = relationship("Alert", back_populates="work_order", foreign_keys=[Alert.work_order_id])
|
||||||
@@ -231,9 +233,9 @@ class EdgeDevice(Base):
|
|||||||
# 扩展
|
# 扩展
|
||||||
extra_info = Column(JSON, nullable=True)
|
extra_info = Column(JSON, nullable=True)
|
||||||
|
|
||||||
created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc))
|
created_at = Column(DateTime, default=lambda: beijing_now())
|
||||||
updated_at = Column(DateTime, default=lambda: datetime.now(timezone.utc),
|
updated_at = Column(DateTime, default=lambda: beijing_now(),
|
||||||
onupdate=lambda: datetime.now(timezone.utc))
|
onupdate=lambda: beijing_now())
|
||||||
|
|
||||||
def to_dict(self) -> dict:
|
def to_dict(self) -> dict:
|
||||||
return {
|
return {
|
||||||
@@ -279,9 +281,9 @@ class AlarmEvent(Base):
|
|||||||
handler = Column(String(64), comment="处理人")
|
handler = Column(String(64), comment="处理人")
|
||||||
handle_remark = Column(Text, comment="处理备注")
|
handle_remark = Column(Text, comment="处理备注")
|
||||||
handled_at = Column(DateTime, comment="处理时间")
|
handled_at = Column(DateTime, comment="处理时间")
|
||||||
created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc))
|
created_at = Column(DateTime, default=lambda: beijing_now())
|
||||||
updated_at = Column(DateTime, default=lambda: datetime.now(timezone.utc),
|
updated_at = Column(DateTime, default=lambda: beijing_now(),
|
||||||
onupdate=lambda: datetime.now(timezone.utc))
|
onupdate=lambda: beijing_now())
|
||||||
|
|
||||||
__table_args__ = (
|
__table_args__ = (
|
||||||
Index('idx_alarm_event_time', 'event_time'),
|
Index('idx_alarm_event_time', 'event_time'),
|
||||||
@@ -323,7 +325,7 @@ class AlarmEventExt(Base):
|
|||||||
ext_type = Column(String(32), comment="扩展类型: EDGE/POST/MANUAL")
|
ext_type = Column(String(32), comment="扩展类型: EDGE/POST/MANUAL")
|
||||||
ext_data = Column(JSON, comment="扩展数据")
|
ext_data = Column(JSON, comment="扩展数据")
|
||||||
roi_config = Column(JSON, comment="ROI配置快照")
|
roi_config = Column(JSON, comment="ROI配置快照")
|
||||||
created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc))
|
created_at = Column(DateTime, default=lambda: beijing_now())
|
||||||
|
|
||||||
def to_dict(self) -> dict:
|
def to_dict(self) -> dict:
|
||||||
return {
|
return {
|
||||||
@@ -349,7 +351,7 @@ class AlarmLlmAnalysis(Base):
|
|||||||
risk_score = Column(Integer, comment="风险评分 0-100")
|
risk_score = Column(Integer, comment="风险评分 0-100")
|
||||||
confidence_score = Column(Float, comment="分析置信度")
|
confidence_score = Column(Float, comment="分析置信度")
|
||||||
suggestion = Column(Text, comment="处置建议")
|
suggestion = Column(Text, comment="处置建议")
|
||||||
created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc))
|
created_at = Column(DateTime, default=lambda: beijing_now())
|
||||||
|
|
||||||
def to_dict(self) -> dict:
|
def to_dict(self) -> dict:
|
||||||
return {
|
return {
|
||||||
@@ -413,8 +415,8 @@ class NotifyArea(Base):
|
|||||||
area_name = Column(String(100), nullable=False)
|
area_name = Column(String(100), nullable=False)
|
||||||
description = Column(String(200), nullable=True)
|
description = Column(String(200), nullable=True)
|
||||||
enabled = Column(SmallInteger, default=1)
|
enabled = Column(SmallInteger, default=1)
|
||||||
created_at = Column(DateTime, default=datetime.now)
|
created_at = Column(DateTime, default=beijing_now)
|
||||||
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
updated_at = Column(DateTime, default=beijing_now, onupdate=beijing_now)
|
||||||
|
|
||||||
|
|
||||||
class CameraAreaBinding(Base):
|
class CameraAreaBinding(Base):
|
||||||
@@ -424,7 +426,7 @@ class CameraAreaBinding(Base):
|
|||||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||||
camera_id = Column(String(64), unique=True, nullable=False, index=True)
|
camera_id = Column(String(64), unique=True, nullable=False, index=True)
|
||||||
area_id = Column(String(36), nullable=False, index=True)
|
area_id = Column(String(36), nullable=False, index=True)
|
||||||
created_at = Column(DateTime, default=datetime.now)
|
created_at = Column(DateTime, default=beijing_now)
|
||||||
|
|
||||||
|
|
||||||
class AreaPersonBinding(Base):
|
class AreaPersonBinding(Base):
|
||||||
@@ -438,8 +440,8 @@ class AreaPersonBinding(Base):
|
|||||||
role = Column(String(20), default="SECURITY")
|
role = Column(String(20), default="SECURITY")
|
||||||
notify_level = Column(Integer, default=1)
|
notify_level = Column(Integer, default=1)
|
||||||
enabled = Column(SmallInteger, default=1)
|
enabled = Column(SmallInteger, default=1)
|
||||||
created_at = Column(DateTime, default=datetime.now)
|
created_at = Column(DateTime, default=beijing_now)
|
||||||
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
updated_at = Column(DateTime, default=beijing_now, onupdate=beijing_now)
|
||||||
|
|
||||||
|
|
||||||
# ==================== 数据库管理 ====================
|
# ==================== 数据库管理 ====================
|
||||||
|
|||||||
20
app/utils/timezone.py
Normal file
20
app/utils/timezone.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
"""
|
||||||
|
时区工具
|
||||||
|
|
||||||
|
本系统所有时间统一使用北京时间(UTC+8)。
|
||||||
|
边缘端上报的 event_time 是北京时间,服务端生成的时间也使用北京时间,
|
||||||
|
确保数据库中所有时间字段语义一致。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from datetime import datetime, timezone, timedelta
|
||||||
|
|
||||||
|
# 北京时区 UTC+8
|
||||||
|
BEIJING_TZ = timezone(timedelta(hours=8))
|
||||||
|
|
||||||
|
|
||||||
|
def beijing_now() -> datetime:
|
||||||
|
"""返回当前北京时间(naive datetime,无时区信息)
|
||||||
|
|
||||||
|
用于数据库存储,与边缘端上报的 event_time 格式一致。
|
||||||
|
"""
|
||||||
|
return datetime.now(BEIJING_TZ).replace(tzinfo=None)
|
||||||
Reference in New Issue
Block a user