Files
zpark-ems/backend/alembic/versions/001_initial_schema.py
Du Wenbo 026c837b91 Squashed 'core/' content from commit 92ec910
git-subtree-dir: core
git-subtree-split: 92ec910a132e379a3a6e442a75bcb07cac0f0010
2026-04-04 18:17:10 +08:00

269 lines
12 KiB
Python

"""Initial schema - all 14 tables
Revision ID: 001_initial
Revises:
Create Date: 2026-04-01
"""
from alembic import op
import sqlalchemy as sa
revision = "001_initial"
down_revision = None
branch_labels = None
depends_on = None
def upgrade() -> None:
# --- roles ---
op.create_table(
"roles",
sa.Column("id", sa.Integer, primary_key=True, autoincrement=True),
sa.Column("name", sa.String(50), unique=True, nullable=False),
sa.Column("display_name", sa.String(100), nullable=False),
sa.Column("description", sa.Text),
sa.Column("permissions", sa.Text),
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
)
# --- users ---
op.create_table(
"users",
sa.Column("id", sa.Integer, primary_key=True, autoincrement=True),
sa.Column("username", sa.String(50), unique=True, nullable=False),
sa.Column("email", sa.String(100), unique=True),
sa.Column("hashed_password", sa.String(200), nullable=False),
sa.Column("full_name", sa.String(100)),
sa.Column("phone", sa.String(20)),
sa.Column("role", sa.String(50), sa.ForeignKey("roles.name"), default="visitor"),
sa.Column("is_active", sa.Boolean, default=True),
sa.Column("last_login", sa.DateTime(timezone=True)),
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
)
op.create_index("ix_users_username", "users", ["username"])
# --- audit_logs ---
op.create_table(
"audit_logs",
sa.Column("id", sa.Integer, primary_key=True, autoincrement=True),
sa.Column("user_id", sa.Integer, sa.ForeignKey("users.id")),
sa.Column("action", sa.String(50), nullable=False),
sa.Column("resource", sa.String(100)),
sa.Column("detail", sa.Text),
sa.Column("ip_address", sa.String(50)),
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
)
# --- device_types ---
op.create_table(
"device_types",
sa.Column("id", sa.Integer, primary_key=True, autoincrement=True),
sa.Column("code", sa.String(50), unique=True, nullable=False),
sa.Column("name", sa.String(100), nullable=False),
sa.Column("icon", sa.String(100)),
sa.Column("data_fields", sa.JSON),
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
)
# --- device_groups ---
op.create_table(
"device_groups",
sa.Column("id", sa.Integer, primary_key=True, autoincrement=True),
sa.Column("name", sa.String(100), nullable=False),
sa.Column("parent_id", sa.Integer, sa.ForeignKey("device_groups.id")),
sa.Column("location", sa.String(200)),
sa.Column("description", sa.Text),
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
)
# --- devices ---
op.create_table(
"devices",
sa.Column("id", sa.Integer, primary_key=True, autoincrement=True),
sa.Column("name", sa.String(100), nullable=False),
sa.Column("code", sa.String(100), unique=True, nullable=False),
sa.Column("device_type", sa.String(50), sa.ForeignKey("device_types.code"), nullable=False),
sa.Column("group_id", sa.Integer, sa.ForeignKey("device_groups.id")),
sa.Column("model", sa.String(100)),
sa.Column("manufacturer", sa.String(100)),
sa.Column("serial_number", sa.String(100)),
sa.Column("rated_power", sa.Float),
sa.Column("install_date", sa.DateTime(timezone=True)),
sa.Column("location", sa.String(200)),
sa.Column("protocol", sa.String(50)),
sa.Column("connection_params", sa.JSON),
sa.Column("collect_interval", sa.Integer, default=15),
sa.Column("status", sa.String(20), default="offline"),
sa.Column("is_active", sa.Boolean, default=True),
sa.Column("metadata", sa.JSON),
sa.Column("last_data_time", sa.DateTime(timezone=True)),
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
)
# --- energy_data ---
op.create_table(
"energy_data",
sa.Column("id", sa.Integer, primary_key=True, autoincrement=True),
sa.Column("device_id", sa.Integer, sa.ForeignKey("devices.id"), nullable=False),
sa.Column("timestamp", sa.DateTime(timezone=True), nullable=False),
sa.Column("data_type", sa.String(50), nullable=False),
sa.Column("value", sa.Float, nullable=False),
sa.Column("unit", sa.String(20)),
sa.Column("quality", sa.Integer, default=0),
sa.Column("raw_data", sa.JSON),
)
op.create_index("ix_energy_data_device_id", "energy_data", ["device_id"])
op.create_index("ix_energy_data_timestamp", "energy_data", ["timestamp"])
# --- energy_daily_summary ---
op.create_table(
"energy_daily_summary",
sa.Column("id", sa.Integer, primary_key=True, autoincrement=True),
sa.Column("device_id", sa.Integer, sa.ForeignKey("devices.id"), nullable=False),
sa.Column("date", sa.DateTime(timezone=True), nullable=False),
sa.Column("energy_type", sa.String(50), nullable=False),
sa.Column("total_consumption", sa.Float, default=0),
sa.Column("total_generation", sa.Float, default=0),
sa.Column("peak_power", sa.Float),
sa.Column("min_power", sa.Float),
sa.Column("avg_power", sa.Float),
sa.Column("operating_hours", sa.Float),
sa.Column("avg_cop", sa.Float),
sa.Column("avg_temperature", sa.Float),
sa.Column("cost", sa.Float),
sa.Column("carbon_emission", sa.Float),
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
)
op.create_index("ix_energy_daily_summary_device_id", "energy_daily_summary", ["device_id"])
op.create_index("ix_energy_daily_summary_date", "energy_daily_summary", ["date"])
# --- alarm_rules ---
op.create_table(
"alarm_rules",
sa.Column("id", sa.Integer, primary_key=True, autoincrement=True),
sa.Column("name", sa.String(200), nullable=False),
sa.Column("device_id", sa.Integer, sa.ForeignKey("devices.id")),
sa.Column("device_type", sa.String(50)),
sa.Column("data_type", sa.String(50), nullable=False),
sa.Column("condition", sa.String(20), nullable=False),
sa.Column("threshold", sa.Float),
sa.Column("threshold_high", sa.Float),
sa.Column("threshold_low", sa.Float),
sa.Column("duration", sa.Integer, default=0),
sa.Column("severity", sa.String(20), default="warning"),
sa.Column("notify_channels", sa.JSON),
sa.Column("notify_targets", sa.JSON),
sa.Column("auto_action", sa.JSON),
sa.Column("silence_start", sa.String(10)),
sa.Column("silence_end", sa.String(10)),
sa.Column("is_active", sa.Boolean, default=True),
sa.Column("created_by", sa.Integer, sa.ForeignKey("users.id")),
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
)
# --- alarm_events ---
op.create_table(
"alarm_events",
sa.Column("id", sa.Integer, primary_key=True, autoincrement=True),
sa.Column("rule_id", sa.Integer, sa.ForeignKey("alarm_rules.id")),
sa.Column("device_id", sa.Integer, sa.ForeignKey("devices.id"), nullable=False),
sa.Column("severity", sa.String(20), nullable=False),
sa.Column("title", sa.String(200), nullable=False),
sa.Column("description", sa.Text),
sa.Column("value", sa.Float),
sa.Column("threshold", sa.Float),
sa.Column("status", sa.String(20), default="active"),
sa.Column("acknowledged_by", sa.Integer, sa.ForeignKey("users.id")),
sa.Column("acknowledged_at", sa.DateTime(timezone=True)),
sa.Column("resolved_at", sa.DateTime(timezone=True)),
sa.Column("resolve_note", sa.Text),
sa.Column("triggered_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
)
# --- emission_factors ---
op.create_table(
"emission_factors",
sa.Column("id", sa.Integer, primary_key=True, autoincrement=True),
sa.Column("name", sa.String(100), nullable=False),
sa.Column("energy_type", sa.String(50), nullable=False),
sa.Column("factor", sa.Float, nullable=False),
sa.Column("unit", sa.String(20), nullable=False),
sa.Column("region", sa.String(50), default="north_china"),
sa.Column("scope", sa.Integer, nullable=False),
sa.Column("source", sa.String(200)),
sa.Column("year", sa.Integer),
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
)
# --- carbon_emissions ---
op.create_table(
"carbon_emissions",
sa.Column("id", sa.Integer, primary_key=True, autoincrement=True),
sa.Column("date", sa.DateTime(timezone=True), nullable=False),
sa.Column("scope", sa.Integer, nullable=False),
sa.Column("category", sa.String(50), nullable=False),
sa.Column("emission", sa.Float, nullable=False),
sa.Column("reduction", sa.Float, default=0),
sa.Column("energy_consumption", sa.Float),
sa.Column("energy_unit", sa.String(20)),
sa.Column("emission_factor_id", sa.Integer),
sa.Column("note", sa.Text),
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
)
op.create_index("ix_carbon_emissions_date", "carbon_emissions", ["date"])
# --- report_templates ---
op.create_table(
"report_templates",
sa.Column("id", sa.Integer, primary_key=True, autoincrement=True),
sa.Column("name", sa.String(100), nullable=False),
sa.Column("report_type", sa.String(50), nullable=False),
sa.Column("description", sa.Text),
sa.Column("fields", sa.JSON, nullable=False),
sa.Column("filters", sa.JSON),
sa.Column("aggregation", sa.String(20), default="sum"),
sa.Column("time_granularity", sa.String(20), default="hour"),
sa.Column("format_config", sa.JSON),
sa.Column("is_system", sa.Boolean, default=False),
sa.Column("created_by", sa.Integer, sa.ForeignKey("users.id")),
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
)
# --- report_tasks ---
op.create_table(
"report_tasks",
sa.Column("id", sa.Integer, primary_key=True, autoincrement=True),
sa.Column("template_id", sa.Integer, sa.ForeignKey("report_templates.id"), nullable=False),
sa.Column("name", sa.String(200)),
sa.Column("schedule", sa.String(50)),
sa.Column("next_run", sa.DateTime(timezone=True)),
sa.Column("last_run", sa.DateTime(timezone=True)),
sa.Column("recipients", sa.JSON),
sa.Column("export_format", sa.String(20), default="xlsx"),
sa.Column("file_path", sa.String(500)),
sa.Column("status", sa.String(20), default="pending"),
sa.Column("is_active", sa.Boolean, default=True),
sa.Column("created_by", sa.Integer, sa.ForeignKey("users.id")),
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
)
def downgrade() -> None:
op.drop_table("report_tasks")
op.drop_table("report_templates")
op.drop_table("carbon_emissions")
op.drop_table("emission_factors")
op.drop_table("alarm_events")
op.drop_table("alarm_rules")
op.drop_table("energy_daily_summary")
op.drop_table("energy_data")
op.drop_table("devices")
op.drop_table("device_groups")
op.drop_table("device_types")
op.drop_table("audit_logs")
op.drop_table("users")
op.drop_table("roles")