Squashed 'core/' content from commit 92ec910

git-subtree-dir: core
git-subtree-split: 92ec910a132e379a3a6e442a75bcb07cac0f0010
This commit is contained in:
Du Wenbo
2026-04-04 18:17:10 +08:00
commit 026c837b91
227 changed files with 39179 additions and 0 deletions

View File

@@ -0,0 +1,43 @@
from app.models.user import User, Role, AuditLog
from app.models.device import Device, DeviceGroup, DeviceType
from app.models.energy import EnergyData, EnergyDailySummary, EnergyCategory
from app.models.alarm import AlarmRule, AlarmEvent
from app.models.carbon import (
CarbonEmission, EmissionFactor, CarbonTarget, CarbonReduction,
GreenCertificate, CarbonReport, CarbonBenchmark,
)
from app.models.report import ReportTemplate, ReportTask
from app.models.setting import SystemSetting
from app.models.charging import (
ChargingStation, ChargingPile, ChargingPriceStrategy, ChargingPriceParam,
ChargingOrder, OccupancyOrder, ChargingBrand, ChargingMerchant,
)
from app.models.quota import EnergyQuota, QuotaUsage
from app.models.pricing import ElectricityPricing, PricingPeriod
from app.models.maintenance import InspectionPlan, InspectionRecord, RepairOrder, DutySchedule
from app.models.management import Regulation, Standard, ProcessDoc, EmergencyPlan
from app.models.prediction import PredictionTask, PredictionResult, OptimizationSchedule
from app.models.energy_strategy import TouPricing, TouPricingPeriod, EnergyStrategy, StrategyExecution, MonthlyCostReport
from app.models.weather import WeatherData, WeatherConfig
from app.models.ai_ops import DeviceHealthScore, AnomalyDetection, DiagnosticReport, MaintenancePrediction, OpsInsight
__all__ = [
"User", "Role", "AuditLog",
"Device", "DeviceGroup", "DeviceType",
"EnergyData", "EnergyDailySummary", "EnergyCategory",
"AlarmRule", "AlarmEvent",
"CarbonEmission", "EmissionFactor", "CarbonTarget", "CarbonReduction",
"GreenCertificate", "CarbonReport", "CarbonBenchmark",
"ReportTemplate", "ReportTask",
"SystemSetting",
"ChargingStation", "ChargingPile", "ChargingPriceStrategy", "ChargingPriceParam",
"ChargingOrder", "OccupancyOrder", "ChargingBrand", "ChargingMerchant",
"EnergyQuota", "QuotaUsage",
"ElectricityPricing", "PricingPeriod",
"InspectionPlan", "InspectionRecord", "RepairOrder", "DutySchedule",
"Regulation", "Standard", "ProcessDoc", "EmergencyPlan",
"PredictionTask", "PredictionResult", "OptimizationSchedule",
"TouPricing", "TouPricingPeriod", "EnergyStrategy", "StrategyExecution", "MonthlyCostReport",
"WeatherData", "WeatherConfig",
"DeviceHealthScore", "AnomalyDetection", "DiagnosticReport", "MaintenancePrediction", "OpsInsight",
]

View File

@@ -0,0 +1,88 @@
"""AI运维智能体数据模型 - 设备健康评分、异常检测、诊断报告、预测性维护、运营洞察"""
from sqlalchemy import Column, Integer, String, Float, Boolean, DateTime, ForeignKey, Text, JSON
from sqlalchemy.sql import func
from app.core.database import Base
class DeviceHealthScore(Base):
"""设备健康评分"""
__tablename__ = "device_health_scores"
id = Column(Integer, primary_key=True, autoincrement=True)
device_id = Column(Integer, ForeignKey("devices.id"), nullable=False, index=True)
timestamp = Column(DateTime(timezone=True), server_default=func.now(), index=True)
health_score = Column(Float, nullable=False) # 0-100
status = Column(String(20), default="healthy") # healthy, warning, critical
factors = Column(JSON) # {power_stability, efficiency, alarm_frequency, uptime, temperature}
trend = Column(String(20), default="stable") # improving, stable, degrading
created_at = Column(DateTime(timezone=True), server_default=func.now())
class AnomalyDetection(Base):
"""异常检测记录"""
__tablename__ = "anomaly_detections"
id = Column(Integer, primary_key=True, autoincrement=True)
device_id = Column(Integer, ForeignKey("devices.id"), nullable=False, index=True)
detected_at = Column(DateTime(timezone=True), server_default=func.now(), index=True)
anomaly_type = Column(String(50), nullable=False) # power_drop, efficiency_loss, abnormal_temperature, communication_loss, pattern_deviation
severity = Column(String(20), default="warning") # info, warning, critical
description = Column(Text)
metric_name = Column(String(50))
expected_value = Column(Float)
actual_value = Column(Float)
deviation_percent = Column(Float)
status = Column(String(20), default="detected") # detected, investigating, resolved, false_positive
resolution_notes = Column(Text)
created_at = Column(DateTime(timezone=True), server_default=func.now())
class DiagnosticReport(Base):
"""AI诊断报告"""
__tablename__ = "diagnostic_reports"
id = Column(Integer, primary_key=True, autoincrement=True)
device_id = Column(Integer, ForeignKey("devices.id"), nullable=False, index=True)
generated_at = Column(DateTime(timezone=True), server_default=func.now(), index=True)
report_type = Column(String(20), default="routine") # routine, triggered, comprehensive
findings = Column(JSON) # [{finding, severity, detail}]
recommendations = Column(JSON) # [{action, priority, detail}]
estimated_impact = Column(JSON) # {energy_loss_kwh, cost_impact_yuan}
status = Column(String(20), default="generated") # generated, reviewed, action_taken
created_at = Column(DateTime(timezone=True), server_default=func.now())
class MaintenancePrediction(Base):
"""预测性维护"""
__tablename__ = "maintenance_predictions"
id = Column(Integer, primary_key=True, autoincrement=True)
device_id = Column(Integer, ForeignKey("devices.id"), nullable=False, index=True)
predicted_at = Column(DateTime(timezone=True), server_default=func.now())
component = Column(String(100))
failure_mode = Column(String(200))
probability = Column(Float) # 0-1
predicted_failure_date = Column(DateTime(timezone=True))
recommended_action = Column(Text)
urgency = Column(String(20), default="medium") # low, medium, high, critical
estimated_downtime_hours = Column(Float)
estimated_repair_cost = Column(Float)
status = Column(String(20), default="predicted") # predicted, scheduled, completed, false_alarm
created_at = Column(DateTime(timezone=True), server_default=func.now())
class OpsInsight(Base):
"""运营洞察"""
__tablename__ = "ops_insights"
id = Column(Integer, primary_key=True, autoincrement=True)
insight_type = Column(String(50), nullable=False) # efficiency_trend, cost_anomaly, performance_comparison, seasonal_pattern
title = Column(String(200), nullable=False)
description = Column(Text)
data = Column(JSON)
impact_level = Column(String(20), default="medium") # low, medium, high
actionable = Column(Boolean, default=False)
recommended_action = Column(Text)
generated_at = Column(DateTime(timezone=True), server_default=func.now())
valid_until = Column(DateTime(timezone=True))
created_at = Column(DateTime(timezone=True), server_default=func.now())

View File

@@ -0,0 +1,48 @@
from sqlalchemy import Column, Integer, String, Float, Boolean, DateTime, ForeignKey, Text, JSON
from sqlalchemy.sql import func
from app.core.database import Base
class AlarmRule(Base):
__tablename__ = "alarm_rules"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(200), nullable=False)
device_id = Column(Integer, ForeignKey("devices.id"))
device_type = Column(String(50)) # 按设备类型的通用规则
data_type = Column(String(50), nullable=False) # 监控的数据类型
condition = Column(String(20), nullable=False) # gt, lt, eq, neq, range_out, rate_of_change
threshold = Column(Float)
threshold_high = Column(Float) # 范围上限
threshold_low = Column(Float) # 范围下限
duration = Column(Integer, default=0) # 持续时间(秒)
severity = Column(String(20), default="warning") # critical, major, warning
notify_channels = Column(JSON) # ["sms", "email", "app", "wechat"]
notify_targets = Column(JSON) # 通知对象
auto_action = Column(JSON) # 联动动作
silence_start = Column(String(10)) # 静默开始时间 HH:MM
silence_end = Column(String(10)) # 静默结束时间
is_active = Column(Boolean, default=True)
created_by = Column(Integer, ForeignKey("users.id"))
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
class AlarmEvent(Base):
__tablename__ = "alarm_events"
id = Column(Integer, primary_key=True, autoincrement=True)
rule_id = Column(Integer, ForeignKey("alarm_rules.id"))
device_id = Column(Integer, ForeignKey("devices.id"), nullable=False)
severity = Column(String(20), nullable=False)
title = Column(String(200), nullable=False)
description = Column(Text)
value = Column(Float) # 触发时的数值
threshold = Column(Float) # 阈值
status = Column(String(20), default="active") # active, acknowledged, resolved
acknowledged_by = Column(Integer, ForeignKey("users.id"))
acknowledged_at = Column(DateTime(timezone=True))
resolved_at = Column(DateTime(timezone=True))
resolve_note = Column(Text)
triggered_at = Column(DateTime(timezone=True), server_default=func.now())
created_at = Column(DateTime(timezone=True), server_default=func.now())

View File

@@ -0,0 +1,115 @@
from sqlalchemy import Column, Integer, String, Float, DateTime, Text, Boolean, Date, JSON, ForeignKey
from sqlalchemy.sql import func
from app.core.database import Base
class EmissionFactor(Base):
"""碳排放因子"""
__tablename__ = "emission_factors"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(100), nullable=False)
energy_type = Column(String(50), nullable=False) # electricity, natural_gas, diesel, etc.
factor = Column(Float, nullable=False) # kgCO2/单位
unit = Column(String(20), nullable=False) # kWh, m³, L, etc.
region = Column(String(50), default="north_china") # 区域电网
scope = Column(Integer, nullable=False) # 1, 2, 3
source = Column(String(200)) # 数据来源
year = Column(Integer)
created_at = Column(DateTime(timezone=True), server_default=func.now())
class CarbonTarget(Base):
"""碳减排目标"""
__tablename__ = "carbon_targets"
id = Column(Integer, primary_key=True, autoincrement=True)
year = Column(Integer, nullable=False)
month = Column(Integer, nullable=True) # NULL for annual target
target_emission_tons = Column(Float, nullable=False)
actual_emission_tons = Column(Float, default=0)
status = Column(String(20), default="on_track") # on_track / warning / exceeded
created_at = Column(DateTime(timezone=True), server_default=func.now())
class CarbonReduction(Base):
"""碳减排活动"""
__tablename__ = "carbon_reductions"
id = Column(Integer, primary_key=True, autoincrement=True)
source_type = Column(String(50), nullable=False) # pv_generation / heat_pump_cop / energy_saving
date = Column(Date, nullable=False, index=True)
reduction_tons = Column(Float, nullable=False)
equivalent_trees = Column(Float, default=0)
methodology = Column(String(200))
verified = Column(Boolean, default=False)
verification_date = Column(DateTime(timezone=True), nullable=True)
device_id = Column(Integer, ForeignKey("devices.id"), nullable=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
class GreenCertificate(Base):
"""绿证管理"""
__tablename__ = "green_certificates"
id = Column(Integer, primary_key=True, autoincrement=True)
certificate_type = Column(String(20), nullable=False) # GEC / IREC / CCER
certificate_number = Column(String(100), unique=True, nullable=False)
issue_date = Column(Date, nullable=False)
expiry_date = Column(Date, nullable=True)
energy_mwh = Column(Float, nullable=False)
price_yuan = Column(Float, default=0)
status = Column(String(20), default="active") # active / used / expired / traded
source_device_id = Column(Integer, ForeignKey("devices.id"), nullable=True)
notes = Column(Text)
created_at = Column(DateTime(timezone=True), server_default=func.now())
class CarbonReport(Base):
"""碳排放报告"""
__tablename__ = "carbon_reports"
id = Column(Integer, primary_key=True, autoincrement=True)
report_type = Column(String(20), nullable=False) # monthly / quarterly / annual
period_start = Column(Date, nullable=False)
period_end = Column(Date, nullable=False)
generated_at = Column(DateTime(timezone=True), server_default=func.now())
scope1_tons = Column(Float, default=0)
scope2_tons = Column(Float, default=0)
scope3_tons = Column(Float, nullable=True)
total_tons = Column(Float, default=0)
reduction_tons = Column(Float, default=0)
net_tons = Column(Float, default=0)
report_data = Column(JSON, nullable=True)
file_path = Column(String(500), nullable=True)
class CarbonBenchmark(Base):
"""行业碳排放基准"""
__tablename__ = "carbon_benchmarks"
id = Column(Integer, primary_key=True, autoincrement=True)
industry = Column(String(100), nullable=False)
metric_name = Column(String(100), nullable=False)
benchmark_value = Column(Float, nullable=False)
unit = Column(String(50), nullable=False)
year = Column(Integer)
source = Column(String(200))
notes = Column(Text)
class CarbonEmission(Base):
"""碳排放记录"""
__tablename__ = "carbon_emissions"
id = Column(Integer, primary_key=True, autoincrement=True)
date = Column(DateTime(timezone=True), nullable=False, index=True)
scope = Column(Integer, nullable=False) # 1, 2, 3
category = Column(String(50), nullable=False) # electricity, gas, heat, etc.
emission = Column(Float, nullable=False) # kgCO2e
reduction = Column(Float, default=0) # 减排量 kgCO2e (光伏、热泵节能等)
energy_consumption = Column(Float) # 对应能耗量
energy_unit = Column(String(20))
emission_factor_id = Column(Integer)
note = Column(Text)
created_at = Column(DateTime(timezone=True), server_default=func.now())

View File

@@ -0,0 +1,145 @@
from sqlalchemy import Column, Integer, String, Float, Boolean, DateTime, ForeignKey, Text, JSON, BigInteger
from sqlalchemy.sql import func
from app.core.database import Base
class ChargingStation(Base):
__tablename__ = "charging_stations"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(200), nullable=False)
merchant_id = Column(Integer, ForeignKey("charging_merchants.id"))
type = Column(String(50)) # public, private, dedicated
address = Column(String(500))
latitude = Column(Float)
longitude = Column(Float)
price = Column(Float) # default price yuan/kWh
activity = Column(Text) # promotions text
status = Column(String(20), default="active") # active, disabled
total_piles = Column(Integer, default=0)
available_piles = Column(Integer, default=0)
total_power_kw = Column(Float, default=0)
photo_url = Column(String(500))
operating_hours = Column(String(100)) # e.g. "00:00-24:00"
created_by = Column(Integer, ForeignKey("users.id"))
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
class ChargingPile(Base):
__tablename__ = "charging_piles"
id = Column(Integer, primary_key=True, autoincrement=True)
station_id = Column(Integer, ForeignKey("charging_stations.id"), nullable=False)
encoding = Column(String(100), unique=True) # terminal code
name = Column(String(200))
type = Column(String(50)) # AC_slow, DC_fast, DC_superfast
brand = Column(String(100))
model = Column(String(100))
rated_power_kw = Column(Float)
connector_type = Column(String(50)) # GB_T, CCS, CHAdeMO
status = Column(String(20), default="active") # active, disabled
work_status = Column(String(20), default="offline") # idle, charging, fault, offline
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
class ChargingPriceStrategy(Base):
__tablename__ = "charging_price_strategies"
id = Column(Integer, primary_key=True, autoincrement=True)
strategy_name = Column(String(200), nullable=False)
station_id = Column(Integer, ForeignKey("charging_stations.id"))
bill_model = Column(String(20)) # tou, flat
description = Column(Text)
status = Column(String(20), default="inactive") # active, inactive
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
class ChargingPriceParam(Base):
__tablename__ = "charging_price_params"
id = Column(Integer, primary_key=True, autoincrement=True)
strategy_id = Column(Integer, ForeignKey("charging_price_strategies.id"), nullable=False)
start_time = Column(String(10), nullable=False) # HH:MM
end_time = Column(String(10), nullable=False)
period_mark = Column(String(20)) # sharp, peak, flat, valley
elec_price = Column(Float, nullable=False) # yuan/kWh
service_price = Column(Float, default=0) # yuan/kWh
created_at = Column(DateTime(timezone=True), server_default=func.now())
class ChargingOrder(Base):
__tablename__ = "charging_orders"
id = Column(Integer, primary_key=True, autoincrement=True)
order_no = Column(String(50), unique=True, nullable=False)
user_id = Column(Integer)
user_name = Column(String(100))
phone = Column(String(20))
station_id = Column(Integer, ForeignKey("charging_stations.id"))
station_name = Column(String(200))
pile_id = Column(Integer, ForeignKey("charging_piles.id"))
pile_name = Column(String(200))
start_time = Column(DateTime(timezone=True))
end_time = Column(DateTime(timezone=True))
car_no = Column(String(20)) # license plate
car_vin = Column(String(50))
charge_method = Column(String(20)) # plug_and_charge, app, card
settle_type = Column(String(20)) # normal, manual, delayed, abnormal, offline
pay_type = Column(String(20)) # balance, wechat, alipay
settle_time = Column(DateTime(timezone=True))
settle_price = Column(Float) # settlement amount
paid_price = Column(Float) # actual paid
discount_amt = Column(Float, default=0)
elec_amt = Column(Float) # electricity fee
serve_amt = Column(Float) # service fee
order_status = Column(String(20), default="charging") # charging, pending_pay, completed, failed, refunded
charge_duration = Column(Integer) # seconds
energy = Column(Float) # kWh delivered
start_soc = Column(Float) # battery start %
end_soc = Column(Float) # battery end %
abno_cause = Column(Text) # abnormal reason
order_source = Column(String(20)) # miniprogram, pc, app
created_at = Column(DateTime(timezone=True), server_default=func.now())
class OccupancyOrder(Base):
__tablename__ = "occupancy_orders"
id = Column(Integer, primary_key=True, autoincrement=True)
order_id = Column(Integer, ForeignKey("charging_orders.id"))
pile_id = Column(Integer, ForeignKey("charging_piles.id"))
start_time = Column(DateTime(timezone=True))
end_time = Column(DateTime(timezone=True))
occupancy_fee = Column(Float, default=0)
status = Column(String(20), default="active")
created_at = Column(DateTime(timezone=True), server_default=func.now())
class ChargingBrand(Base):
__tablename__ = "charging_brands"
id = Column(Integer, primary_key=True, autoincrement=True)
brand_name = Column(String(100), nullable=False)
logo_url = Column(String(500))
country = Column(String(50))
description = Column(Text)
created_at = Column(DateTime(timezone=True), server_default=func.now())
class ChargingMerchant(Base):
__tablename__ = "charging_merchants"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(200), nullable=False)
contact_person = Column(String(100))
phone = Column(String(20))
email = Column(String(100))
address = Column(String(500))
business_license = Column(String(100))
status = Column(String(20), default="active")
settlement_type = Column(String(20)) # prepaid, postpaid
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())

View File

@@ -0,0 +1,51 @@
from sqlalchemy import Column, Integer, String, Float, Boolean, DateTime, ForeignKey, Text, JSON
from sqlalchemy.sql import func
from app.core.database import Base
class DeviceType(Base):
__tablename__ = "device_types"
id = Column(Integer, primary_key=True, autoincrement=True)
code = Column(String(50), unique=True, nullable=False) # pv_inverter, heat_pump, solar_thermal, battery, meter, sensor
name = Column(String(100), nullable=False)
icon = Column(String(100))
data_fields = Column(JSON) # 该类型设备的数据字段定义
created_at = Column(DateTime(timezone=True), server_default=func.now())
class DeviceGroup(Base):
__tablename__ = "device_groups"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(100), nullable=False)
parent_id = Column(Integer, ForeignKey("device_groups.id"))
location = Column(String(200))
description = Column(Text)
created_at = Column(DateTime(timezone=True), server_default=func.now())
class Device(Base):
__tablename__ = "devices"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(100), nullable=False)
code = Column(String(100), unique=True, nullable=False) # 设备编号
device_type = Column(String(50), ForeignKey("device_types.code"), nullable=False)
group_id = Column(Integer, ForeignKey("device_groups.id"))
model = Column(String(100)) # 型号
manufacturer = Column(String(100)) # 厂商
serial_number = Column(String(100)) # 序列号
rated_power = Column(Float) # 额定功率 kW
install_date = Column(DateTime(timezone=True))
location = Column(String(200))
protocol = Column(String(50)) # modbus_tcp, modbus_rtu, opc_ua, mqtt, http_api
connection_params = Column(JSON) # 连接参数 (IP, port, slave_id, etc.)
collect_interval = Column(Integer, default=15) # 采集间隔(秒)
category_id = Column(Integer, ForeignKey("energy_categories.id")) # 分项类别
status = Column(String(20), default="offline") # online, offline, alarm, maintenance
is_active = Column(Boolean, default=True)
metadata_ = Column("metadata", JSON) # 扩展元数据
last_data_time = Column(DateTime(timezone=True))
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())

View File

@@ -0,0 +1,52 @@
from sqlalchemy import Column, Integer, String, Float, DateTime, ForeignKey, JSON
from sqlalchemy.sql import func
from app.core.database import Base
class EnergyCategory(Base):
"""能耗分项类别"""
__tablename__ = "energy_categories"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(100), nullable=False) # HVAC, 照明, 动力, 特殊
code = Column(String(50), unique=True, nullable=False) # hvac, lighting, power, special
parent_id = Column(Integer, ForeignKey("energy_categories.id"))
sort_order = Column(Integer, default=0)
icon = Column(String(100))
color = Column(String(20)) # hex color for charts
created_at = Column(DateTime(timezone=True), server_default=func.now())
class EnergyData(Base):
"""时序能耗采集数据 - 使用TimescaleDB hypertable"""
__tablename__ = "energy_data"
id = Column(Integer, primary_key=True, autoincrement=True)
device_id = Column(Integer, ForeignKey("devices.id"), nullable=False, index=True)
timestamp = Column(DateTime(timezone=True), nullable=False, index=True)
data_type = Column(String(50), nullable=False) # power, energy, temperature, flow, etc.
value = Column(Float, nullable=False)
unit = Column(String(20)) # kW, kWh, ℃, m³/h, etc.
quality = Column(Integer, default=0) # 0=good, 1=interpolated, 2=suspect
raw_data = Column(JSON) # 原始完整数据包
class EnergyDailySummary(Base):
"""每日能耗汇总"""
__tablename__ = "energy_daily_summary"
id = Column(Integer, primary_key=True, autoincrement=True)
device_id = Column(Integer, ForeignKey("devices.id"), nullable=False, index=True)
date = Column(DateTime(timezone=True), nullable=False, index=True)
energy_type = Column(String(50), nullable=False) # electricity, heat, water, gas
total_consumption = Column(Float, default=0) # 总消耗
total_generation = Column(Float, default=0) # 总产出
peak_power = Column(Float) # 最大功率
min_power = Column(Float) # 最小功率
avg_power = Column(Float) # 平均功率
operating_hours = Column(Float) # 运行小时数
avg_cop = Column(Float) # 平均COP (热泵)
avg_temperature = Column(Float) # 平均温度
cost = Column(Float) # 费用
carbon_emission = Column(Float) # 碳排放 kgCO2
created_at = Column(DateTime(timezone=True), server_default=func.now())

View File

@@ -0,0 +1,81 @@
from sqlalchemy import Column, Integer, String, Float, Boolean, DateTime, ForeignKey, JSON, Date
from sqlalchemy.sql import func
from app.core.database import Base
class TouPricing(Base):
"""分时电价配置 (Time-of-Use pricing)"""
__tablename__ = "tou_pricing"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(200), nullable=False)
region = Column(String(100), default="北京")
effective_date = Column(Date)
end_date = Column(Date)
is_active = Column(Boolean, default=True)
created_by = Column(Integer, ForeignKey("users.id"))
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
class TouPricingPeriod(Base):
"""分时电价时段 (TOU pricing periods)"""
__tablename__ = "tou_pricing_periods"
id = Column(Integer, primary_key=True, autoincrement=True)
pricing_id = Column(Integer, ForeignKey("tou_pricing.id", ondelete="CASCADE"), nullable=False)
period_type = Column(String(20), nullable=False) # sharp_peak, peak, flat, valley
start_time = Column(String(10), nullable=False) # HH:MM
end_time = Column(String(10), nullable=False) # HH:MM
price_yuan_per_kwh = Column(Float, nullable=False)
month_range = Column(String(50)) # e.g. "1-3,11-12" for winter, null=all
created_at = Column(DateTime(timezone=True), server_default=func.now())
class EnergyStrategy(Base):
"""能源优化策略"""
__tablename__ = "energy_strategies"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(200), nullable=False)
strategy_type = Column(String(50), nullable=False) # heat_storage, load_shift, pv_priority
description = Column(String(500))
parameters = Column(JSON, default=dict)
is_enabled = Column(Boolean, default=False)
priority = Column(Integer, default=0)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
class StrategyExecution(Base):
"""策略执行记录"""
__tablename__ = "strategy_executions"
id = Column(Integer, primary_key=True, autoincrement=True)
strategy_id = Column(Integer, ForeignKey("energy_strategies.id"), nullable=False)
date = Column(Date, nullable=False)
actions_taken = Column(JSON, default=list)
savings_kwh = Column(Float, default=0)
savings_yuan = Column(Float, default=0)
status = Column(String(20), default="planned") # planned, executing, completed
created_at = Column(DateTime(timezone=True), server_default=func.now())
class MonthlyCostReport(Base):
"""月度电费分析报告"""
__tablename__ = "monthly_cost_reports"
id = Column(Integer, primary_key=True, autoincrement=True)
year_month = Column(String(7), nullable=False, unique=True) # YYYY-MM
total_consumption_kwh = Column(Float, default=0)
total_cost_yuan = Column(Float, default=0)
peak_consumption = Column(Float, default=0)
valley_consumption = Column(Float, default=0)
flat_consumption = Column(Float, default=0)
sharp_peak_consumption = Column(Float, default=0)
pv_self_consumption = Column(Float, default=0)
pv_feed_in = Column(Float, default=0)
optimized_cost = Column(Float, default=0)
baseline_cost = Column(Float, default=0)
savings_yuan = Column(Float, default=0)
created_at = Column(DateTime(timezone=True), server_default=func.now())

View File

@@ -0,0 +1,69 @@
from sqlalchemy import Column, Integer, String, Float, Boolean, DateTime, ForeignKey, Text, JSON
from sqlalchemy.sql import func
from app.core.database import Base
class InspectionPlan(Base):
__tablename__ = "inspection_plans"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(200), nullable=False)
description = Column(Text)
device_group_id = Column(Integer, ForeignKey("device_groups.id"))
device_ids = Column(JSON) # specific devices to inspect
schedule_type = Column(String(20)) # daily, weekly, monthly, custom
schedule_cron = Column(String(100)) # cron expression for custom
inspector_id = Column(Integer, ForeignKey("users.id"))
checklist = Column(JSON) # [{item: "检查外观", required: true, type: "checkbox"}]
is_active = Column(Boolean, default=True)
next_run_at = Column(DateTime(timezone=True))
created_by = Column(Integer, ForeignKey("users.id"))
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
class InspectionRecord(Base):
__tablename__ = "inspection_records"
id = Column(Integer, primary_key=True, autoincrement=True)
plan_id = Column(Integer, ForeignKey("inspection_plans.id"), nullable=False)
inspector_id = Column(Integer, ForeignKey("users.id"), nullable=False)
status = Column(String(20), default="pending") # pending, in_progress, completed, issues_found
findings = Column(JSON) # [{item, result, note, photo_url}]
started_at = Column(DateTime(timezone=True))
completed_at = Column(DateTime(timezone=True))
created_at = Column(DateTime(timezone=True), server_default=func.now())
class RepairOrder(Base):
__tablename__ = "repair_orders"
id = Column(Integer, primary_key=True, autoincrement=True)
code = Column(String(50), unique=True, nullable=False) # WO-20260402-001
title = Column(String(200), nullable=False)
description = Column(Text)
device_id = Column(Integer, ForeignKey("devices.id"))
alarm_event_id = Column(Integer, ForeignKey("alarm_events.id"))
priority = Column(String(20), default="medium") # critical, high, medium, low
status = Column(String(20), default="open") # open, assigned, in_progress, completed, verified, closed
assigned_to = Column(Integer, ForeignKey("users.id"))
resolution = Column(Text)
cost_estimate = Column(Float)
actual_cost = Column(Float)
created_by = Column(Integer, ForeignKey("users.id"))
created_at = Column(DateTime(timezone=True), server_default=func.now())
assigned_at = Column(DateTime(timezone=True))
completed_at = Column(DateTime(timezone=True))
closed_at = Column(DateTime(timezone=True))
class DutySchedule(Base):
__tablename__ = "duty_schedules"
id = Column(Integer, primary_key=True, autoincrement=True)
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
duty_date = Column(DateTime(timezone=True), nullable=False)
shift = Column(String(20)) # day, night, on_call
area_id = Column(Integer, ForeignKey("device_groups.id"))
notes = Column(Text)
created_at = Column(DateTime(timezone=True), server_default=func.now())

View File

@@ -0,0 +1,60 @@
from sqlalchemy import Column, Integer, String, DateTime, Text, Boolean, ForeignKey, JSON
from sqlalchemy.sql import func
from app.core.database import Base
class Regulation(Base):
__tablename__ = "regulations"
id = Column(Integer, primary_key=True, autoincrement=True)
title = Column(String(200), nullable=False)
category = Column(String(50)) # safety, operation, quality, environment
content = Column(Text)
effective_date = Column(DateTime(timezone=True))
status = Column(String(20), default="active") # active, archived, draft
attachment_url = Column(String(500))
created_by = Column(Integer, ForeignKey("users.id"))
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
class Standard(Base):
__tablename__ = "standards"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(200), nullable=False)
code = Column(String(100)) # e.g. ISO 50001, GB/T 23331
type = Column(String(50)) # national, industry, enterprise
description = Column(Text)
compliance_status = Column(String(20), default="pending") # compliant, non_compliant, pending, in_progress
review_date = Column(DateTime(timezone=True))
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
class ProcessDoc(Base):
__tablename__ = "process_docs"
id = Column(Integer, primary_key=True, autoincrement=True)
title = Column(String(200), nullable=False)
category = Column(String(50)) # operation, maintenance, emergency, training
content = Column(Text)
version = Column(String(20), default="1.0")
approved_by = Column(String(100))
effective_date = Column(DateTime(timezone=True))
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
class EmergencyPlan(Base):
__tablename__ = "emergency_plans"
id = Column(Integer, primary_key=True, autoincrement=True)
title = Column(String(200), nullable=False)
scenario = Column(String(100)) # fire, power_outage, equipment_failure, chemical_leak
steps = Column(JSON) # [{step_number, action, responsible_person, contact}]
responsible_person = Column(String(100))
review_date = Column(DateTime(timezone=True))
is_active = Column(Boolean, default=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())

View File

@@ -0,0 +1,48 @@
from sqlalchemy import Column, Integer, String, Float, DateTime, ForeignKey, JSON, Text
from sqlalchemy.sql import func
from app.core.database import Base
class PredictionTask(Base):
"""AI预测任务元数据"""
__tablename__ = "prediction_tasks"
id = Column(Integer, primary_key=True, autoincrement=True)
device_id = Column(Integer, ForeignKey("devices.id"))
prediction_type = Column(String(50), nullable=False) # pv, load, heatpump, optimization
horizon_hours = Column(Integer, default=24)
status = Column(String(20), default="pending") # pending, running, completed, failed
parameters = Column(JSON) # extra config for the prediction run
error_message = Column(Text)
created_at = Column(DateTime(timezone=True), server_default=func.now())
completed_at = Column(DateTime(timezone=True))
class PredictionResult(Base):
"""AI预测结果时序数据"""
__tablename__ = "prediction_results"
id = Column(Integer, primary_key=True, autoincrement=True)
task_id = Column(Integer, ForeignKey("prediction_tasks.id"), nullable=False, index=True)
timestamp = Column(DateTime(timezone=True), nullable=False, index=True)
predicted_value = Column(Float, nullable=False)
confidence_lower = Column(Float)
confidence_upper = Column(Float)
actual_value = Column(Float) # filled later for accuracy tracking
unit = Column(String(20))
class OptimizationSchedule(Base):
"""AI优化调度建议"""
__tablename__ = "optimization_schedules"
id = Column(Integer, primary_key=True, autoincrement=True)
device_id = Column(Integer, ForeignKey("devices.id"))
date = Column(DateTime(timezone=True), nullable=False, index=True)
schedule_data = Column(JSON) # hourly on/off + setpoints
expected_savings_kwh = Column(Float, default=0)
expected_savings_yuan = Column(Float, default=0)
status = Column(String(20), default="pending") # pending, approved, executed, rejected
approved_by = Column(Integer, ForeignKey("users.id"))
approved_at = Column(DateTime(timezone=True))
created_at = Column(DateTime(timezone=True), server_default=func.now())

View File

@@ -0,0 +1,33 @@
from sqlalchemy import Column, Integer, String, Float, Boolean, DateTime, ForeignKey, JSON
from sqlalchemy.sql import func
from app.core.database import Base
class ElectricityPricing(Base):
"""电价配置"""
__tablename__ = "electricity_pricing"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(200), nullable=False)
energy_type = Column(String(50), default="electricity") # electricity, heat, water, gas
pricing_type = Column(String(20), nullable=False) # flat, tou, tiered
effective_from = Column(DateTime(timezone=True))
effective_to = Column(DateTime(timezone=True))
is_active = Column(Boolean, default=True)
created_by = Column(Integer, ForeignKey("users.id"))
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
class PricingPeriod(Base):
"""分时电价时段"""
__tablename__ = "pricing_periods"
id = Column(Integer, primary_key=True, autoincrement=True)
pricing_id = Column(Integer, ForeignKey("electricity_pricing.id"), nullable=False)
period_name = Column(String(50), nullable=False) # peak, valley, flat, shoulder, sharp
start_time = Column(String(10), nullable=False) # HH:MM format
end_time = Column(String(10), nullable=False) # HH:MM format
price_per_unit = Column(Float, nullable=False) # yuan per kWh
applicable_months = Column(JSON) # [1,2,3,...12] or null for all months
created_at = Column(DateTime(timezone=True), server_default=func.now())

View File

@@ -0,0 +1,38 @@
from sqlalchemy import Column, Integer, String, Float, Boolean, DateTime, ForeignKey
from sqlalchemy.sql import func
from app.core.database import Base
class EnergyQuota(Base):
"""能源配额管理"""
__tablename__ = "energy_quotas"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(200), nullable=False)
target_type = Column(String(50), nullable=False) # building, department, device_group
target_id = Column(Integer, nullable=False) # FK to device_groups.id
energy_type = Column(String(50), nullable=False) # electricity, heat, water, gas
period = Column(String(20), nullable=False) # monthly, yearly
quota_value = Column(Float, nullable=False) # 目标消耗值
unit = Column(String(20), default="kWh")
warning_threshold_pct = Column(Float, default=80) # 80%预警
alert_threshold_pct = Column(Float, default=95) # 95%告警
is_active = Column(Boolean, default=True)
created_by = Column(Integer, ForeignKey("users.id"))
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
class QuotaUsage(Base):
"""配额使用记录"""
__tablename__ = "quota_usage"
id = Column(Integer, primary_key=True, autoincrement=True)
quota_id = Column(Integer, ForeignKey("energy_quotas.id"), nullable=False)
period_start = Column(DateTime(timezone=True), nullable=False)
period_end = Column(DateTime(timezone=True), nullable=False)
actual_value = Column(Float, default=0)
quota_value = Column(Float, nullable=False)
usage_rate_pct = Column(Float, default=0)
status = Column(String(20), default="normal") # normal, warning, exceeded
calculated_at = Column(DateTime(timezone=True), server_default=func.now())

View File

@@ -0,0 +1,38 @@
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, Text, JSON
from sqlalchemy.sql import func
from app.core.database import Base
class ReportTemplate(Base):
__tablename__ = "report_templates"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(100), nullable=False)
report_type = Column(String(50), nullable=False) # daily, weekly, monthly, yearly, custom
description = Column(Text)
fields = Column(JSON, nullable=False) # 报表字段配置
filters = Column(JSON) # 默认筛选条件
aggregation = Column(String(20), default="sum") # sum, avg, max, min
time_granularity = Column(String(20), default="hour") # hour, day, month
format_config = Column(JSON) # 展示格式配置
is_system = Column(Boolean, default=False) # 系统预置
created_by = Column(Integer, ForeignKey("users.id"))
created_at = Column(DateTime(timezone=True), server_default=func.now())
class ReportTask(Base):
__tablename__ = "report_tasks"
id = Column(Integer, primary_key=True, autoincrement=True)
template_id = Column(Integer, ForeignKey("report_templates.id"), nullable=False)
name = Column(String(200))
schedule = Column(String(50)) # cron expression or null for manual
next_run = Column(DateTime(timezone=True))
last_run = Column(DateTime(timezone=True))
recipients = Column(JSON) # 接收人
export_format = Column(String(20), default="xlsx") # xlsx, csv, pdf
file_path = Column(String(500)) # 最新生成的文件路径
status = Column(String(20), default="pending") # pending, running, completed, failed
is_active = Column(Boolean, default=True)
created_by = Column(Integer, ForeignKey("users.id"))
created_at = Column(DateTime(timezone=True), server_default=func.now())

View File

@@ -0,0 +1,13 @@
from sqlalchemy import Column, Integer, String, Text, DateTime
from sqlalchemy.sql import func
from app.core.database import Base
class SystemSetting(Base):
__tablename__ = "system_settings"
id = Column(Integer, primary_key=True, autoincrement=True)
key = Column(String(100), unique=True, nullable=False, index=True)
value = Column(Text, nullable=False, default="")
description = Column(String(255))
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())

View File

@@ -0,0 +1,42 @@
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, Text
from sqlalchemy.sql import func
from app.core.database import Base
class Role(Base):
__tablename__ = "roles"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(50), unique=True, nullable=False) # admin, energy_manager, area_manager, operator, analyst, visitor
display_name = Column(String(100), nullable=False)
description = Column(Text)
permissions = Column(Text) # JSON string of permission list
created_at = Column(DateTime(timezone=True), server_default=func.now())
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, autoincrement=True)
username = Column(String(50), unique=True, nullable=False, index=True)
email = Column(String(100), unique=True)
hashed_password = Column(String(200), nullable=False)
full_name = Column(String(100))
phone = Column(String(20))
role = Column(String(50), ForeignKey("roles.name"), default="visitor")
is_active = Column(Boolean, default=True)
last_login = Column(DateTime(timezone=True))
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
class AuditLog(Base):
__tablename__ = "audit_logs"
id = Column(Integer, primary_key=True, autoincrement=True)
user_id = Column(Integer, ForeignKey("users.id"))
action = Column(String(50), nullable=False)
resource = Column(String(100))
detail = Column(Text)
ip_address = Column(String(50))
created_at = Column(DateTime(timezone=True), server_default=func.now())

View File

@@ -0,0 +1,33 @@
from sqlalchemy import Column, Integer, String, Float, Boolean, DateTime
from sqlalchemy.sql import func
from app.core.database import Base
class WeatherData(Base):
"""气象数据缓存"""
__tablename__ = "weather_data"
id = Column(Integer, primary_key=True, autoincrement=True)
timestamp = Column(DateTime(timezone=True), nullable=False, index=True)
data_type = Column(String(20), nullable=False) # observation, forecast
temperature = Column(Float)
humidity = Column(Float)
solar_radiation = Column(Float) # W/m2
cloud_cover = Column(Float) # 0-100 %
wind_speed = Column(Float) # m/s
source = Column(String(20), default="mock") # api, mock
fetched_at = Column(DateTime(timezone=True), server_default=func.now())
class WeatherConfig(Base):
"""气象API配置"""
__tablename__ = "weather_config"
id = Column(Integer, primary_key=True, autoincrement=True)
api_provider = Column(String(50), default="mock")
api_key = Column(String(200))
location_lat = Column(Float, default=39.9)
location_lon = Column(Float, default=116.4)
fetch_interval_minutes = Column(Integer, default=30)
is_enabled = Column(Boolean, default=True)
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())