ems-core v1.0.0: Standard EMS platform core

Shared backend + frontend for multi-customer EMS deployments.
- 12 enterprise modules: quota, cost, charging, maintenance, analysis, etc.
- 120+ API endpoints, 37 database tables
- Customer config mechanism (CUSTOMER env var + YAML config)
- Collectors: Modbus TCP, MQTT, HTTP API, Sungrow iSolarCloud
- Frontend: React 19 + Ant Design + ECharts + Three.js
- Infrastructure: Redis cache, rate limiting, aggregation engine

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Du Wenbo
2026-04-04 18:14:11 +08:00
commit 92ec910a13
227 changed files with 39179 additions and 0 deletions

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())