Merge commit '026c837b919ab4380e8a6e6c052364bbf9bbe8a3' as 'core'
This commit is contained in:
43
core/backend/app/models/__init__.py
Normal file
43
core/backend/app/models/__init__.py
Normal 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",
|
||||
]
|
||||
88
core/backend/app/models/ai_ops.py
Normal file
88
core/backend/app/models/ai_ops.py
Normal 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())
|
||||
48
core/backend/app/models/alarm.py
Normal file
48
core/backend/app/models/alarm.py
Normal 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())
|
||||
115
core/backend/app/models/carbon.py
Normal file
115
core/backend/app/models/carbon.py
Normal 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())
|
||||
145
core/backend/app/models/charging.py
Normal file
145
core/backend/app/models/charging.py
Normal 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())
|
||||
51
core/backend/app/models/device.py
Normal file
51
core/backend/app/models/device.py
Normal 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())
|
||||
52
core/backend/app/models/energy.py
Normal file
52
core/backend/app/models/energy.py
Normal 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())
|
||||
81
core/backend/app/models/energy_strategy.py
Normal file
81
core/backend/app/models/energy_strategy.py
Normal 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())
|
||||
69
core/backend/app/models/maintenance.py
Normal file
69
core/backend/app/models/maintenance.py
Normal 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())
|
||||
60
core/backend/app/models/management.py
Normal file
60
core/backend/app/models/management.py
Normal 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())
|
||||
48
core/backend/app/models/prediction.py
Normal file
48
core/backend/app/models/prediction.py
Normal 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())
|
||||
33
core/backend/app/models/pricing.py
Normal file
33
core/backend/app/models/pricing.py
Normal 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())
|
||||
38
core/backend/app/models/quota.py
Normal file
38
core/backend/app/models/quota.py
Normal 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())
|
||||
38
core/backend/app/models/report.py
Normal file
38
core/backend/app/models/report.py
Normal 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())
|
||||
13
core/backend/app/models/setting.py
Normal file
13
core/backend/app/models/setting.py
Normal 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())
|
||||
42
core/backend/app/models/user.py
Normal file
42
core/backend/app/models/user.py
Normal 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())
|
||||
33
core/backend/app/models/weather.py
Normal file
33
core/backend/app/models/weather.py
Normal 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())
|
||||
Reference in New Issue
Block a user