refactor
This commit is contained in:
@@ -1,14 +1,11 @@
|
||||
# coding=utf-8
|
||||
"""
|
||||
@Time :2026/7/29
|
||||
@Author :tao.chen
|
||||
@Author :tao.chen
|
||||
"""
|
||||
|
||||
from sqlalchemy.orm import DeclarativeBase
|
||||
|
||||
|
||||
class Base(DeclarativeBase):
|
||||
pass
|
||||
|
||||
# 导入所有实体模型,确保 Base.metadata 能收集到所有表
|
||||
# from common.db.models.notebook import NotebookModel
|
||||
# from common.db.models.workspace import WorkspaceModel
|
||||
# from common.db.models.job import JobRunModel
|
||||
@@ -1,878 +0,0 @@
|
||||
from typing import Optional
|
||||
import datetime
|
||||
import decimal
|
||||
|
||||
from sqlalchemy import BINARY, BigInteger, CHAR, DECIMAL, Double, ForeignKeyConstraint, Index, Integer, JSON, String, Text, text
|
||||
from sqlalchemy.dialects.mysql import BIGINT, DATETIME, INTEGER, SMALLINT, TINYINT
|
||||
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
|
||||
|
||||
class Base(DeclarativeBase):
|
||||
pass
|
||||
|
||||
|
||||
class ConsumerInbox(Base):
|
||||
__tablename__ = 'consumer_inbox'
|
||||
__table_args__ = (
|
||||
Index('idx_consumer_inbox_status', 'consumer_name', 'process_status', 'created_at'),
|
||||
{'comment': '消费者幂等 Inbox,防止 Stream 重投导致重复执行'}
|
||||
)
|
||||
|
||||
consumer_name: Mapped[str] = mapped_column(String(128), primary_key=True)
|
||||
event_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
process_status: Mapped[str] = mapped_column(String(16), nullable=False, server_default=text("'processing'"), comment='processing/succeeded/failed')
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3)'))
|
||||
message_id: Mapped[Optional[str]] = mapped_column(String(128), comment='Redis Stream message ID')
|
||||
processed_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
error_message: Mapped[Optional[str]] = mapped_column(String(2000))
|
||||
|
||||
|
||||
class OutboxEvents(Base):
|
||||
__tablename__ = 'outbox_events'
|
||||
__table_args__ = (
|
||||
Index('idx_outbox_aggregate', 'aggregate_type', 'aggregate_id', 'created_at'),
|
||||
Index('idx_outbox_idempotency', 'idempotency_key'),
|
||||
Index('idx_outbox_pending', 'event_status', 'available_at', 'created_at'),
|
||||
{'comment': '事务 Outbox;提交后发布到 Redis Streams'}
|
||||
)
|
||||
|
||||
event_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
aggregate_type: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
aggregate_id: Mapped[str] = mapped_column(String(128), nullable=False)
|
||||
event_type: Mapped[str] = mapped_column(String(128), nullable=False)
|
||||
schema_version: Mapped[int] = mapped_column(SMALLINT, nullable=False, server_default=text("1"))
|
||||
payload_json: Mapped[dict] = mapped_column(JSON, nullable=False)
|
||||
event_status: Mapped[str] = mapped_column(String(16), nullable=False, server_default=text("'pending'"), comment='pending/published/failed')
|
||||
available_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3)'))
|
||||
retry_count: Mapped[int] = mapped_column(INTEGER, nullable=False, server_default=text("0"))
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3)'))
|
||||
trace_id: Mapped[Optional[str]] = mapped_column(String(64))
|
||||
idempotency_key: Mapped[Optional[str]] = mapped_column(String(128))
|
||||
published_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
last_error: Mapped[Optional[str]] = mapped_column(String(2000))
|
||||
|
||||
|
||||
class Permissions(Base):
|
||||
__tablename__ = 'permissions'
|
||||
__table_args__ = (
|
||||
Index('idx_permissions_module', 'module_code'),
|
||||
Index('uk_permissions_code', 'permission_code', unique=True),
|
||||
{'comment': '权限点'}
|
||||
)
|
||||
|
||||
permission_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
permission_code: Mapped[str] = mapped_column(String(128), nullable=False)
|
||||
permission_name: Mapped[str] = mapped_column(String(100), nullable=False)
|
||||
module_code: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3)'))
|
||||
description: Mapped[Optional[str]] = mapped_column(String(500))
|
||||
|
||||
role_permissions: Mapped[list['RolePermissions']] = relationship('RolePermissions', back_populates='permission')
|
||||
|
||||
|
||||
class Roles(Base):
|
||||
__tablename__ = 'roles'
|
||||
__table_args__ = (
|
||||
Index('uk_roles_code', 'role_code', unique=True),
|
||||
{'comment': '角色'}
|
||||
)
|
||||
|
||||
role_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
role_code: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
role_name: Mapped[str] = mapped_column(String(100), nullable=False)
|
||||
role_scope: Mapped[str] = mapped_column(String(16), nullable=False, comment='platform/workspace')
|
||||
is_builtin: Mapped[int] = mapped_column(TINYINT(1), nullable=False, server_default=text("0"))
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3)'))
|
||||
updated_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)'))
|
||||
description: Mapped[Optional[str]] = mapped_column(String(500))
|
||||
|
||||
role_permissions: Mapped[list['RolePermissions']] = relationship('RolePermissions', back_populates='role')
|
||||
users: Mapped[list['Users']] = relationship('Users', back_populates='platform_role')
|
||||
workspace_members: Mapped[list['WorkspaceMembers']] = relationship('WorkspaceMembers', back_populates='role')
|
||||
|
||||
|
||||
class RolePermissions(Base):
|
||||
__tablename__ = 'role_permissions'
|
||||
__table_args__ = (
|
||||
ForeignKeyConstraint(['permission_id'], ['permissions.permission_id'], ondelete='CASCADE', name='fk_role_permissions_permission'),
|
||||
ForeignKeyConstraint(['role_id'], ['roles.role_id'], ondelete='CASCADE', name='fk_role_permissions_role'),
|
||||
Index('fk_role_permissions_permission', 'permission_id'),
|
||||
{'comment': '角色权限'}
|
||||
)
|
||||
|
||||
role_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
permission_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3)'))
|
||||
|
||||
permission: Mapped['Permissions'] = relationship('Permissions', back_populates='role_permissions')
|
||||
role: Mapped['Roles'] = relationship('Roles', back_populates='role_permissions')
|
||||
|
||||
|
||||
class Users(Base):
|
||||
__tablename__ = 'users'
|
||||
__table_args__ = (
|
||||
ForeignKeyConstraint(['platform_role_id'], ['roles.role_id'], ondelete='SET NULL', name='fk_users_platform_role'),
|
||||
Index('fk_users_platform_role', 'platform_role_id'),
|
||||
Index('idx_users_status', 'status'),
|
||||
Index('uk_users_email', 'email', unique=True),
|
||||
Index('uk_users_username', 'username', unique=True),
|
||||
{'comment': '平台用户'}
|
||||
)
|
||||
|
||||
user_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
username: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
display_name: Mapped[str] = mapped_column(String(100), nullable=False)
|
||||
password_hash: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
status: Mapped[str] = mapped_column(String(16), nullable=False, server_default=text("'active'"), comment='active/disabled/locked')
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3)'))
|
||||
updated_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)'))
|
||||
email: Mapped[Optional[str]] = mapped_column(String(255))
|
||||
platform_role_id: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
avatar_uri: Mapped[Optional[str]] = mapped_column(String(1000))
|
||||
last_login_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
deleted_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
|
||||
platform_role: Mapped[Optional['Roles']] = relationship('Roles', back_populates='users')
|
||||
workspaces: Mapped[list['Workspaces']] = relationship('Workspaces', back_populates='users')
|
||||
audit_logs: Mapped[list['AuditLogs']] = relationship('AuditLogs', back_populates='actor_user')
|
||||
runtime_instances: Mapped[list['RuntimeInstances']] = relationship('RuntimeInstances', foreign_keys='[RuntimeInstances.owner_user_id]', back_populates='owner_user')
|
||||
runtime_instances_: Mapped[list['RuntimeInstances']] = relationship('RuntimeInstances', foreign_keys='[RuntimeInstances.started_by]', back_populates='users')
|
||||
schedules: Mapped[list['Schedules']] = relationship('Schedules', foreign_keys='[Schedules.created_by]', back_populates='users')
|
||||
schedules_: Mapped[list['Schedules']] = relationship('Schedules', foreign_keys='[Schedules.updated_by]', back_populates='users_')
|
||||
storage_objects: Mapped[list['StorageObjects']] = relationship('StorageObjects', foreign_keys='[StorageObjects.created_by]', back_populates='users')
|
||||
storage_objects_: Mapped[list['StorageObjects']] = relationship('StorageObjects', foreign_keys='[StorageObjects.owner_user_id]', back_populates='owner_user')
|
||||
workspace_members: Mapped[list['WorkspaceMembers']] = relationship('WorkspaceMembers', back_populates='user')
|
||||
data_resources: Mapped[list['DataResources']] = relationship('DataResources', back_populates='owner_user')
|
||||
edit_sessions: Mapped[list['EditSessions']] = relationship('EditSessions', back_populates='user')
|
||||
schedule_runs: Mapped[list['ScheduleRuns']] = relationship('ScheduleRuns', back_populates='users')
|
||||
scripts: Mapped[list['Scripts']] = relationship('Scripts', back_populates='owner_user')
|
||||
upload_sessions: Mapped[list['UploadSessions']] = relationship('UploadSessions', back_populates='user')
|
||||
workspace_operations: Mapped[list['WorkspaceOperations']] = relationship('WorkspaceOperations', back_populates='users')
|
||||
notebook_snapshots: Mapped[list['NotebookSnapshots']] = relationship('NotebookSnapshots', back_populates='users')
|
||||
versions: Mapped[list['Versions']] = relationship('Versions', back_populates='users')
|
||||
experiments: Mapped[list['Experiments']] = relationship('Experiments', back_populates='owner_user')
|
||||
|
||||
|
||||
class Workspaces(Base):
|
||||
__tablename__ = 'workspaces'
|
||||
__table_args__ = (
|
||||
ForeignKeyConstraint(['created_by'], ['users.user_id'], ondelete='RESTRICT', name='fk_workspaces_created_by'),
|
||||
Index('fk_workspaces_created_by', 'created_by'),
|
||||
Index('idx_workspaces_status', 'status'),
|
||||
Index('uk_workspaces_code', 'workspace_code', unique=True),
|
||||
{'comment': 'Workspace'}
|
||||
)
|
||||
|
||||
workspace_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
workspace_code: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
workspace_name: Mapped[str] = mapped_column(String(150), nullable=False)
|
||||
active_root_uri: Mapped[str] = mapped_column(String(1500), nullable=False, comment='活动工作区,建议 NFS/PVC/file URI')
|
||||
quota_bytes: Mapped[int] = mapped_column(BIGINT, nullable=False, server_default=text("0"), comment='0 表示不限额')
|
||||
used_bytes: Mapped[int] = mapped_column(BIGINT, nullable=False, server_default=text("0"))
|
||||
status: Mapped[str] = mapped_column(String(24), nullable=False, server_default=text("'active'"), comment='creating/active/suspended/deleting/deleted')
|
||||
created_by: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3)'))
|
||||
updated_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)'))
|
||||
description: Mapped[Optional[str]] = mapped_column(String(1000))
|
||||
artifact_bucket: Mapped[Optional[str]] = mapped_column(String(128), comment='RustFS bucket')
|
||||
artifact_prefix: Mapped[Optional[str]] = mapped_column(String(512), comment='RustFS object key prefix')
|
||||
deleted_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
|
||||
users: Mapped['Users'] = relationship('Users', back_populates='workspaces')
|
||||
audit_logs: Mapped[list['AuditLogs']] = relationship('AuditLogs', back_populates='workspace')
|
||||
runtime_instances: Mapped[list['RuntimeInstances']] = relationship('RuntimeInstances', back_populates='workspace')
|
||||
schedules: Mapped[list['Schedules']] = relationship('Schedules', back_populates='workspace')
|
||||
storage_objects: Mapped[list['StorageObjects']] = relationship('StorageObjects', back_populates='workspace')
|
||||
workspace_members: Mapped[list['WorkspaceMembers']] = relationship('WorkspaceMembers', back_populates='workspace')
|
||||
data_resources: Mapped[list['DataResources']] = relationship('DataResources', back_populates='workspace')
|
||||
edit_sessions: Mapped[list['EditSessions']] = relationship('EditSessions', back_populates='workspace')
|
||||
schedule_runs: Mapped[list['ScheduleRuns']] = relationship('ScheduleRuns', back_populates='workspace')
|
||||
scripts: Mapped[list['Scripts']] = relationship('Scripts', back_populates='workspace')
|
||||
upload_sessions: Mapped[list['UploadSessions']] = relationship('UploadSessions', back_populates='workspace')
|
||||
workspace_operations: Mapped[list['WorkspaceOperations']] = relationship('WorkspaceOperations', back_populates='workspace')
|
||||
notebook_snapshots: Mapped[list['NotebookSnapshots']] = relationship('NotebookSnapshots', back_populates='workspace')
|
||||
versions: Mapped[list['Versions']] = relationship('Versions', back_populates='workspace')
|
||||
experiments: Mapped[list['Experiments']] = relationship('Experiments', back_populates='workspace')
|
||||
|
||||
|
||||
class AuditLogs(Base):
|
||||
__tablename__ = 'audit_logs'
|
||||
__table_args__ = (
|
||||
ForeignKeyConstraint(['actor_user_id'], ['users.user_id'], ondelete='SET NULL', name='fk_audit_actor'),
|
||||
ForeignKeyConstraint(['workspace_id'], ['workspaces.workspace_id'], ondelete='SET NULL', name='fk_audit_workspace'),
|
||||
Index('idx_audit_action_time', 'action_code', 'created_at'),
|
||||
Index('idx_audit_actor_time', 'actor_user_id', 'created_at'),
|
||||
Index('idx_audit_workspace_time', 'workspace_id', 'created_at'),
|
||||
{'comment': '操作审计日志'}
|
||||
)
|
||||
|
||||
audit_id: Mapped[int] = mapped_column(BIGINT, primary_key=True)
|
||||
action_code: Mapped[str] = mapped_column(String(128), nullable=False)
|
||||
target_type: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
operation_status: Mapped[str] = mapped_column(String(16), nullable=False, server_default=text("'success'"))
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3)'))
|
||||
workspace_id: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
actor_user_id: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
target_id: Mapped[Optional[str]] = mapped_column(String(128))
|
||||
client_ip: Mapped[Optional[str]] = mapped_column(String(45))
|
||||
user_agent: Mapped[Optional[str]] = mapped_column(String(1000))
|
||||
detail_json: Mapped[Optional[dict]] = mapped_column(JSON)
|
||||
|
||||
actor_user: Mapped[Optional['Users']] = relationship('Users', back_populates='audit_logs')
|
||||
workspace: Mapped[Optional['Workspaces']] = relationship('Workspaces', back_populates='audit_logs')
|
||||
|
||||
|
||||
class RuntimeInstances(Base):
|
||||
__tablename__ = 'runtime_instances'
|
||||
__table_args__ = (
|
||||
ForeignKeyConstraint(['owner_user_id'], ['users.user_id'], ondelete='SET NULL', name='fk_runtime_owner'),
|
||||
ForeignKeyConstraint(['started_by'], ['users.user_id'], ondelete='RESTRICT', name='fk_runtime_started_by'),
|
||||
ForeignKeyConstraint(['workspace_id'], ['workspaces.workspace_id'], ondelete='RESTRICT', name='fk_runtime_workspace'),
|
||||
Index('fk_runtime_started_by', 'started_by'),
|
||||
Index('idx_runtime_lease', 'actual_state', 'lease_expires_at'),
|
||||
Index('idx_runtime_owner_state', 'owner_user_id', 'actual_state'),
|
||||
Index('idx_runtime_workspace_state', 'workspace_id', 'runtime_type', 'actual_state'),
|
||||
{'comment': 'Jupyter/未来 VS Code、OpenCode Runtime 实例'}
|
||||
)
|
||||
|
||||
runtime_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
workspace_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
runtime_type: Mapped[str] = mapped_column(String(24), nullable=False, server_default=text("'jupyter'"))
|
||||
runtime_provider: Mapped[str] = mapped_column(String(24), nullable=False, comment='process/docker/kubernetes')
|
||||
proxy_base_path: Mapped[str] = mapped_column(String(512), nullable=False)
|
||||
desired_state: Mapped[str] = mapped_column(String(16), nullable=False, server_default=text("'running'"))
|
||||
actual_state: Mapped[str] = mapped_column(String(24), nullable=False, server_default=text("'provisioning'"), comment='provisioning/starting/running/unhealthy/stopping/stopped/failed')
|
||||
state_version: Mapped[int] = mapped_column(INTEGER, nullable=False, server_default=text("0"), comment='乐观锁版本')
|
||||
started_by: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3)'))
|
||||
updated_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)'))
|
||||
owner_user_id: Mapped[Optional[str]] = mapped_column(CHAR(26), comment='为空表示 Workspace 级 Runtime')
|
||||
runtime_ref: Mapped[Optional[str]] = mapped_column(String(255), comment='PID/container ID/pod UID')
|
||||
host_node: Mapped[Optional[str]] = mapped_column(String(255))
|
||||
internal_url: Mapped[Optional[str]] = mapped_column(String(1000))
|
||||
started_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
last_heartbeat_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
lease_expires_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
stopped_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
error_message: Mapped[Optional[str]] = mapped_column(Text)
|
||||
|
||||
owner_user: Mapped[Optional['Users']] = relationship('Users', foreign_keys=[owner_user_id], back_populates='runtime_instances')
|
||||
users: Mapped['Users'] = relationship('Users', foreign_keys=[started_by], back_populates='runtime_instances_')
|
||||
workspace: Mapped['Workspaces'] = relationship('Workspaces', back_populates='runtime_instances')
|
||||
edit_sessions: Mapped[list['EditSessions']] = relationship('EditSessions', back_populates='runtime')
|
||||
workspace_operations: Mapped[list['WorkspaceOperations']] = relationship('WorkspaceOperations', back_populates='runtime')
|
||||
|
||||
|
||||
class Schedules(Base):
|
||||
__tablename__ = 'schedules'
|
||||
__table_args__ = (
|
||||
ForeignKeyConstraint(['created_by'], ['users.user_id'], ondelete='RESTRICT', name='fk_schedules_created_by'),
|
||||
ForeignKeyConstraint(['updated_by'], ['users.user_id'], ondelete='RESTRICT', name='fk_schedules_updated_by'),
|
||||
ForeignKeyConstraint(['workspace_id'], ['workspaces.workspace_id'], ondelete='RESTRICT', name='fk_schedules_workspace'),
|
||||
Index('fk_schedules_created_by', 'created_by'),
|
||||
Index('fk_schedules_updated_by', 'updated_by'),
|
||||
Index('idx_schedules_due', 'enabled', 'next_run_at'),
|
||||
Index('idx_schedules_workspace', 'workspace_id', 'enabled', 'updated_at'),
|
||||
{'comment': '调度方案'}
|
||||
)
|
||||
|
||||
schedule_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
workspace_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
schedule_name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
trigger_type: Mapped[str] = mapped_column(String(16), nullable=False, server_default=text("'cron'"), comment='manual/cron/api')
|
||||
timezone: Mapped[str] = mapped_column(String(64), nullable=False, server_default=text("'Asia/Shanghai'"))
|
||||
enabled: Mapped[int] = mapped_column(TINYINT(1), nullable=False, server_default=text("0"))
|
||||
workflow_version: Mapped[int] = mapped_column(INTEGER, nullable=False, server_default=text("1"))
|
||||
max_concurrency: Mapped[int] = mapped_column(INTEGER, nullable=False, server_default=text("1"))
|
||||
failure_policy: Mapped[str] = mapped_column(String(24), nullable=False, server_default=text("'stop'"), comment='stop/continue')
|
||||
created_by: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
updated_by: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3)'))
|
||||
updated_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)'))
|
||||
description: Mapped[Optional[str]] = mapped_column(String(1000))
|
||||
cron_expression: Mapped[Optional[str]] = mapped_column(String(128))
|
||||
last_run_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
next_run_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
deleted_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
|
||||
users: Mapped['Users'] = relationship('Users', foreign_keys=[created_by], back_populates='schedules')
|
||||
users_: Mapped['Users'] = relationship('Users', foreign_keys=[updated_by], back_populates='schedules_')
|
||||
workspace: Mapped['Workspaces'] = relationship('Workspaces', back_populates='schedules')
|
||||
schedule_runs: Mapped[list['ScheduleRuns']] = relationship('ScheduleRuns', back_populates='schedule')
|
||||
schedule_nodes: Mapped[list['ScheduleNodes']] = relationship('ScheduleNodes', back_populates='schedule')
|
||||
schedule_edges: Mapped[list['ScheduleEdges']] = relationship('ScheduleEdges', back_populates='schedule')
|
||||
|
||||
|
||||
class StorageObjects(Base):
|
||||
__tablename__ = 'storage_objects'
|
||||
__table_args__ = (
|
||||
ForeignKeyConstraint(['created_by'], ['users.user_id'], ondelete='RESTRICT', name='fk_storage_created_by'),
|
||||
ForeignKeyConstraint(['owner_user_id'], ['users.user_id'], ondelete='SET NULL', name='fk_storage_owner'),
|
||||
ForeignKeyConstraint(['parent_object_id'], ['storage_objects.storage_object_id'], ondelete='SET NULL', name='fk_storage_parent'),
|
||||
ForeignKeyConstraint(['workspace_id'], ['workspaces.workspace_id'], ondelete='RESTRICT', name='fk_storage_workspace'),
|
||||
Index('fk_storage_created_by', 'created_by'),
|
||||
Index('idx_storage_content_hash', 'content_hash'),
|
||||
Index('idx_storage_owner', 'owner_user_id', 'object_status'),
|
||||
Index('idx_storage_parent', 'parent_object_id'),
|
||||
Index('idx_storage_workspace_usage', 'workspace_id', 'usage_type', 'object_status'),
|
||||
Index('uk_storage_bucket_key', 'storage_backend', 'bucket_name', 'object_key_hash', unique=True),
|
||||
Index('uk_storage_workspace_path', 'workspace_id', 'storage_backend', 'path_hash', unique=True),
|
||||
{'comment': 'Workspace 文件和 RustFS 对象的统一元数据'}
|
||||
)
|
||||
|
||||
storage_object_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
workspace_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
object_type: Mapped[str] = mapped_column(String(16), nullable=False, comment='file/directory')
|
||||
usage_type: Mapped[str] = mapped_column(String(32), nullable=False, comment='working_copy/public_script/data_resource/version_artifact/snapshot/run_log/run_result')
|
||||
storage_backend: Mapped[str] = mapped_column(String(16), nullable=False, comment='workspace_fs/rustfs')
|
||||
storage_uri: Mapped[str] = mapped_column(String(1500), nullable=False)
|
||||
file_name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
size_bytes: Mapped[int] = mapped_column(BIGINT, nullable=False, server_default=text("0"))
|
||||
visibility: Mapped[str] = mapped_column(String(16), nullable=False, server_default=text("'private'"), comment='private/workspace/public')
|
||||
is_immutable: Mapped[int] = mapped_column(TINYINT(1), nullable=False, server_default=text("0"))
|
||||
object_status: Mapped[str] = mapped_column(String(24), nullable=False, server_default=text("'available'"), comment='uploading/available/deleting/deleted/failed')
|
||||
created_by: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3)'))
|
||||
updated_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)'))
|
||||
owner_user_id: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
parent_object_id: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
relative_path: Mapped[Optional[str]] = mapped_column(String(1024), comment='Workspace 相对路径')
|
||||
path_hash: Mapped[Optional[bytes]] = mapped_column(BINARY(32), comment='SHA-256(relative_path),由应用写入')
|
||||
bucket_name: Mapped[Optional[str]] = mapped_column(String(128))
|
||||
object_key: Mapped[Optional[str]] = mapped_column(String(1024))
|
||||
object_key_hash: Mapped[Optional[bytes]] = mapped_column(BINARY(32), comment='SHA-256(object_key),由应用写入')
|
||||
file_extension: Mapped[Optional[str]] = mapped_column(String(32))
|
||||
mime_type: Mapped[Optional[str]] = mapped_column(String(255))
|
||||
content_hash: Mapped[Optional[str]] = mapped_column(CHAR(64), comment='SHA-256 hex')
|
||||
object_etag: Mapped[Optional[str]] = mapped_column(String(255))
|
||||
deleted_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
|
||||
users: Mapped['Users'] = relationship('Users', foreign_keys=[created_by], back_populates='storage_objects')
|
||||
owner_user: Mapped[Optional['Users']] = relationship('Users', foreign_keys=[owner_user_id], back_populates='storage_objects_')
|
||||
parent_object: Mapped[Optional['StorageObjects']] = relationship('StorageObjects', remote_side=[storage_object_id], back_populates='parent_object_reverse')
|
||||
parent_object_reverse: Mapped[list['StorageObjects']] = relationship('StorageObjects', remote_side=[parent_object_id], back_populates='parent_object')
|
||||
workspace: Mapped['Workspaces'] = relationship('Workspaces', back_populates='storage_objects')
|
||||
data_resources: Mapped[list['DataResources']] = relationship('DataResources', back_populates='storage_object')
|
||||
edit_sessions: Mapped[list['EditSessions']] = relationship('EditSessions', back_populates='storage_object')
|
||||
schedule_runs: Mapped[list['ScheduleRuns']] = relationship('ScheduleRuns', foreign_keys='[ScheduleRuns.logs_object_id]', back_populates='logs_object')
|
||||
schedule_runs_: Mapped[list['ScheduleRuns']] = relationship('ScheduleRuns', foreign_keys='[ScheduleRuns.result_object_id]', back_populates='result_object')
|
||||
scripts: Mapped[list['Scripts']] = relationship('Scripts', back_populates='current_object')
|
||||
upload_sessions: Mapped[list['UploadSessions']] = relationship('UploadSessions', back_populates='storage_object')
|
||||
notebook_snapshots: Mapped[list['NotebookSnapshots']] = relationship('NotebookSnapshots', foreign_keys='[NotebookSnapshots.artifact_object_id]', back_populates='artifact_object')
|
||||
notebook_snapshots_: Mapped[list['NotebookSnapshots']] = relationship('NotebookSnapshots', foreign_keys='[NotebookSnapshots.source_object_id]', back_populates='source_object')
|
||||
versions: Mapped[list['Versions']] = relationship('Versions', foreign_keys='[Versions.artifact_object_id]', back_populates='artifact_object')
|
||||
versions_: Mapped[list['Versions']] = relationship('Versions', foreign_keys='[Versions.source_object_id]', back_populates='source_object')
|
||||
experiments: Mapped[list['Experiments']] = relationship('Experiments', foreign_keys='[Experiments.logs_object_id]', back_populates='logs_object')
|
||||
experiments_: Mapped[list['Experiments']] = relationship('Experiments', foreign_keys='[Experiments.result_object_id]', back_populates='result_object')
|
||||
schedule_node_runs: Mapped[list['ScheduleNodeRuns']] = relationship('ScheduleNodeRuns', foreign_keys='[ScheduleNodeRuns.logs_object_id]', back_populates='logs_object')
|
||||
schedule_node_runs_: Mapped[list['ScheduleNodeRuns']] = relationship('ScheduleNodeRuns', foreign_keys='[ScheduleNodeRuns.result_object_id]', back_populates='result_object')
|
||||
|
||||
|
||||
class WorkspaceMembers(Base):
|
||||
__tablename__ = 'workspace_members'
|
||||
__table_args__ = (
|
||||
ForeignKeyConstraint(['role_id'], ['roles.role_id'], ondelete='RESTRICT', name='fk_workspace_members_role'),
|
||||
ForeignKeyConstraint(['user_id'], ['users.user_id'], ondelete='RESTRICT', name='fk_workspace_members_user'),
|
||||
ForeignKeyConstraint(['workspace_id'], ['workspaces.workspace_id'], ondelete='CASCADE', name='fk_workspace_members_workspace'),
|
||||
Index('idx_workspace_members_role', 'role_id'),
|
||||
Index('idx_workspace_members_user', 'user_id', 'member_status'),
|
||||
{'comment': 'Workspace 成员与角色'}
|
||||
)
|
||||
|
||||
workspace_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
user_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
role_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
member_status: Mapped[str] = mapped_column(String(16), nullable=False, server_default=text("'active'"))
|
||||
joined_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3)'))
|
||||
updated_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)'))
|
||||
|
||||
role: Mapped['Roles'] = relationship('Roles', back_populates='workspace_members')
|
||||
user: Mapped['Users'] = relationship('Users', back_populates='workspace_members')
|
||||
workspace: Mapped['Workspaces'] = relationship('Workspaces', back_populates='workspace_members')
|
||||
|
||||
|
||||
class DataResources(Base):
|
||||
__tablename__ = 'data_resources'
|
||||
__table_args__ = (
|
||||
ForeignKeyConstraint(['owner_user_id'], ['users.user_id'], ondelete='RESTRICT', name='fk_data_resources_owner'),
|
||||
ForeignKeyConstraint(['storage_object_id'], ['storage_objects.storage_object_id'], ondelete='RESTRICT', name='fk_data_resources_object'),
|
||||
ForeignKeyConstraint(['workspace_id'], ['workspaces.workspace_id'], ondelete='RESTRICT', name='fk_data_resources_workspace'),
|
||||
Index('idx_data_resources_owner', 'owner_user_id', 'status'),
|
||||
Index('idx_data_resources_workspace', 'workspace_id', 'visibility', 'status'),
|
||||
Index('uk_data_resources_object', 'storage_object_id', unique=True),
|
||||
{'comment': '数据资源'}
|
||||
)
|
||||
|
||||
resource_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
workspace_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
storage_object_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
owner_user_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
resource_name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
visibility: Mapped[str] = mapped_column(String(16), nullable=False, server_default=text("'private'"))
|
||||
status: Mapped[str] = mapped_column(String(16), nullable=False, server_default=text("'active'"))
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3)'))
|
||||
updated_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)'))
|
||||
description: Mapped[Optional[str]] = mapped_column(String(1000))
|
||||
schema_json: Mapped[Optional[dict]] = mapped_column(JSON, comment='字段结构、行数等可选元数据')
|
||||
deleted_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
|
||||
owner_user: Mapped['Users'] = relationship('Users', back_populates='data_resources')
|
||||
storage_object: Mapped['StorageObjects'] = relationship('StorageObjects', back_populates='data_resources')
|
||||
workspace: Mapped['Workspaces'] = relationship('Workspaces', back_populates='data_resources')
|
||||
experiment_resources: Mapped[list['ExperimentResources']] = relationship('ExperimentResources', back_populates='resource')
|
||||
|
||||
|
||||
class EditSessions(Base):
|
||||
__tablename__ = 'edit_sessions'
|
||||
__table_args__ = (
|
||||
ForeignKeyConstraint(['runtime_id'], ['runtime_instances.runtime_id'], ondelete='SET NULL', name='fk_edit_sessions_runtime'),
|
||||
ForeignKeyConstraint(['storage_object_id'], ['storage_objects.storage_object_id'], ondelete='RESTRICT', name='fk_edit_sessions_object'),
|
||||
ForeignKeyConstraint(['user_id'], ['users.user_id'], ondelete='RESTRICT', name='fk_edit_sessions_user'),
|
||||
ForeignKeyConstraint(['workspace_id'], ['workspaces.workspace_id'], ondelete='RESTRICT', name='fk_edit_sessions_workspace'),
|
||||
Index('fk_edit_sessions_workspace', 'workspace_id'),
|
||||
Index('idx_edit_sessions_object', 'storage_object_id', 'session_status', 'expires_at'),
|
||||
Index('idx_edit_sessions_runtime', 'runtime_id', 'session_status'),
|
||||
Index('idx_edit_sessions_user', 'user_id', 'session_status'),
|
||||
{'comment': '编辑会话审计;实时锁状态以 Redis 为准'}
|
||||
)
|
||||
|
||||
edit_session_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
workspace_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
storage_object_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
user_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
redis_lock_key: Mapped[str] = mapped_column(String(512), nullable=False)
|
||||
lock_token_hash: Mapped[bytes] = mapped_column(BINARY(32), nullable=False, comment='不保存原始 token')
|
||||
session_status: Mapped[str] = mapped_column(String(16), nullable=False, server_default=text("'active'"), comment='active/closed/expired/failed')
|
||||
started_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3)'))
|
||||
last_heartbeat_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3)'))
|
||||
expires_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False)
|
||||
runtime_id: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
jupyter_session_id: Mapped[Optional[str]] = mapped_column(String(255))
|
||||
ended_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
end_reason: Mapped[Optional[str]] = mapped_column(String(64))
|
||||
|
||||
runtime: Mapped[Optional['RuntimeInstances']] = relationship('RuntimeInstances', back_populates='edit_sessions')
|
||||
storage_object: Mapped['StorageObjects'] = relationship('StorageObjects', back_populates='edit_sessions')
|
||||
user: Mapped['Users'] = relationship('Users', back_populates='edit_sessions')
|
||||
workspace: Mapped['Workspaces'] = relationship('Workspaces', back_populates='edit_sessions')
|
||||
|
||||
|
||||
class ScheduleRuns(Base):
|
||||
__tablename__ = 'schedule_runs'
|
||||
__table_args__ = (
|
||||
ForeignKeyConstraint(['logs_object_id'], ['storage_objects.storage_object_id'], ondelete='SET NULL', name='fk_schedule_runs_logs'),
|
||||
ForeignKeyConstraint(['result_object_id'], ['storage_objects.storage_object_id'], ondelete='SET NULL', name='fk_schedule_runs_result'),
|
||||
ForeignKeyConstraint(['schedule_id'], ['schedules.schedule_id'], ondelete='RESTRICT', name='fk_schedule_runs_schedule'),
|
||||
ForeignKeyConstraint(['triggered_by'], ['users.user_id'], ondelete='SET NULL', name='fk_schedule_runs_user'),
|
||||
ForeignKeyConstraint(['workspace_id'], ['workspaces.workspace_id'], ondelete='RESTRICT', name='fk_schedule_runs_workspace'),
|
||||
Index('fk_schedule_runs_logs', 'logs_object_id'),
|
||||
Index('fk_schedule_runs_result', 'result_object_id'),
|
||||
Index('fk_schedule_runs_user', 'triggered_by'),
|
||||
Index('idx_schedule_runs_schedule', 'schedule_id', 'created_at'),
|
||||
Index('idx_schedule_runs_status', 'run_status', 'queued_at'),
|
||||
Index('idx_schedule_runs_workspace_status', 'workspace_id', 'run_status', 'queued_at'),
|
||||
Index('uk_schedule_runs_idempotency', 'idempotency_key', unique=True),
|
||||
{'comment': '调度运行'}
|
||||
)
|
||||
|
||||
run_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
schedule_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
workspace_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
workflow_version: Mapped[int] = mapped_column(INTEGER, nullable=False)
|
||||
trigger_type: Mapped[str] = mapped_column(String(16), nullable=False, comment='manual/cron/api/retry')
|
||||
idempotency_key: Mapped[str] = mapped_column(String(128), nullable=False)
|
||||
run_status: Mapped[str] = mapped_column(String(24), nullable=False, server_default=text("'queued'"), comment='queued/running/succeeded/failed/cancelled/timed_out')
|
||||
state_version: Mapped[int] = mapped_column(INTEGER, nullable=False, server_default=text("0"), comment='乐观锁版本')
|
||||
schedule_snapshot: Mapped[dict] = mapped_column(JSON, nullable=False, comment='执行时 DAG 快照')
|
||||
queued_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3)'))
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3)'))
|
||||
triggered_by: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
started_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
finished_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
duration_ms: Mapped[Optional[int]] = mapped_column(BIGINT)
|
||||
error_code: Mapped[Optional[str]] = mapped_column(String(64))
|
||||
error_message: Mapped[Optional[str]] = mapped_column(Text)
|
||||
logs_object_id: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
result_object_id: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
|
||||
logs_object: Mapped[Optional['StorageObjects']] = relationship('StorageObjects', foreign_keys=[logs_object_id], back_populates='schedule_runs')
|
||||
result_object: Mapped[Optional['StorageObjects']] = relationship('StorageObjects', foreign_keys=[result_object_id], back_populates='schedule_runs_')
|
||||
schedule: Mapped['Schedules'] = relationship('Schedules', back_populates='schedule_runs')
|
||||
users: Mapped[Optional['Users']] = relationship('Users', back_populates='schedule_runs')
|
||||
workspace: Mapped['Workspaces'] = relationship('Workspaces', back_populates='schedule_runs')
|
||||
experiments: Mapped[list['Experiments']] = relationship('Experiments', back_populates='schedule_run')
|
||||
schedule_node_runs: Mapped[list['ScheduleNodeRuns']] = relationship('ScheduleNodeRuns', back_populates='run')
|
||||
|
||||
|
||||
class Scripts(Base):
|
||||
__tablename__ = 'scripts'
|
||||
__table_args__ = (
|
||||
ForeignKeyConstraint(['current_object_id'], ['storage_objects.storage_object_id'], ondelete='RESTRICT', name='fk_scripts_current_object'),
|
||||
ForeignKeyConstraint(['owner_user_id'], ['users.user_id'], ondelete='RESTRICT', name='fk_scripts_owner'),
|
||||
ForeignKeyConstraint(['workspace_id'], ['workspaces.workspace_id'], ondelete='RESTRICT', name='fk_scripts_workspace'),
|
||||
Index('idx_scripts_owner', 'owner_user_id', 'status'),
|
||||
Index('idx_scripts_workspace', 'workspace_id', 'script_type', 'visibility', 'status'),
|
||||
Index('uk_scripts_current_object', 'current_object_id', unique=True),
|
||||
{'comment': '可执行 Python/Notebook 脚本'}
|
||||
)
|
||||
|
||||
script_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
workspace_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
current_object_id: Mapped[str] = mapped_column(CHAR(26), nullable=False, comment='当前工作副本')
|
||||
owner_user_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
script_name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
script_type: Mapped[str] = mapped_column(String(16), nullable=False, comment='python/notebook')
|
||||
visibility: Mapped[str] = mapped_column(String(16), nullable=False, server_default=text("'private'"))
|
||||
status: Mapped[str] = mapped_column(String(16), nullable=False, server_default=text("'active'"))
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3)'))
|
||||
updated_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)'))
|
||||
deleted_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
|
||||
current_object: Mapped['StorageObjects'] = relationship('StorageObjects', back_populates='scripts')
|
||||
owner_user: Mapped['Users'] = relationship('Users', back_populates='scripts')
|
||||
workspace: Mapped['Workspaces'] = relationship('Workspaces', back_populates='scripts')
|
||||
notebook_snapshots: Mapped[list['NotebookSnapshots']] = relationship('NotebookSnapshots', back_populates='script')
|
||||
versions: Mapped[list['Versions']] = relationship('Versions', back_populates='script')
|
||||
experiments: Mapped[list['Experiments']] = relationship('Experiments', back_populates='script')
|
||||
|
||||
|
||||
class UploadSessions(Base):
|
||||
__tablename__ = 'upload_sessions'
|
||||
__table_args__ = (
|
||||
ForeignKeyConstraint(['storage_object_id'], ['storage_objects.storage_object_id'], ondelete='SET NULL', name='fk_upload_sessions_storage_object'),
|
||||
ForeignKeyConstraint(['user_id'], ['users.user_id'], ondelete='RESTRICT', name='fk_upload_sessions_user'),
|
||||
ForeignKeyConstraint(['workspace_id'], ['workspaces.workspace_id'], ondelete='RESTRICT', name='fk_upload_sessions_workspace'),
|
||||
Index('fk_upload_sessions_storage_object', 'storage_object_id'),
|
||||
Index('fk_upload_sessions_user', 'user_id'),
|
||||
Index('idx_upload_sessions_expiry', 'upload_status', 'expires_at'),
|
||||
Index('idx_upload_sessions_object_key', 'bucket_name', 'object_key_hash'),
|
||||
Index('idx_upload_sessions_workspace', 'workspace_id', 'user_id', 'created_at'),
|
||||
Index('uk_upload_sessions_idempotency', 'idempotency_key', unique=True),
|
||||
{'comment': 'RustFS 预签名上传会话;URL 本身不持久化'}
|
||||
)
|
||||
|
||||
upload_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
workspace_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
user_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
idempotency_key: Mapped[str] = mapped_column(String(128), nullable=False)
|
||||
bucket_name: Mapped[str] = mapped_column(String(128), nullable=False)
|
||||
object_key: Mapped[str] = mapped_column(String(1024), nullable=False)
|
||||
object_key_hash: Mapped[bytes] = mapped_column(BINARY(32), nullable=False)
|
||||
upload_status: Mapped[str] = mapped_column(String(24), nullable=False, server_default=text("'created'"), comment='created/uploading/completed/expired/aborted/failed')
|
||||
expires_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False)
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3)'))
|
||||
updated_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)'))
|
||||
multipart_upload_id: Mapped[Optional[str]] = mapped_column(String(255))
|
||||
expected_size_bytes: Mapped[Optional[int]] = mapped_column(BIGINT)
|
||||
expected_hash: Mapped[Optional[str]] = mapped_column(CHAR(64))
|
||||
content_type: Mapped[Optional[str]] = mapped_column(String(255))
|
||||
storage_object_id: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
completed_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
|
||||
storage_object: Mapped[Optional['StorageObjects']] = relationship('StorageObjects', back_populates='upload_sessions')
|
||||
user: Mapped['Users'] = relationship('Users', back_populates='upload_sessions')
|
||||
workspace: Mapped['Workspaces'] = relationship('Workspaces', back_populates='upload_sessions')
|
||||
|
||||
|
||||
class WorkspaceOperations(Base):
|
||||
__tablename__ = 'workspace_operations'
|
||||
__table_args__ = (
|
||||
ForeignKeyConstraint(['requested_by'], ['users.user_id'], ondelete='RESTRICT', name='fk_workspace_operations_user'),
|
||||
ForeignKeyConstraint(['runtime_id'], ['runtime_instances.runtime_id'], ondelete='SET NULL', name='fk_workspace_operations_runtime'),
|
||||
ForeignKeyConstraint(['workspace_id'], ['workspaces.workspace_id'], ondelete='RESTRICT', name='fk_workspace_operations_workspace'),
|
||||
Index('fk_workspace_operations_user', 'requested_by'),
|
||||
Index('idx_workspace_operations_runtime', 'runtime_id', 'created_at'),
|
||||
Index('idx_workspace_operations_workspace', 'workspace_id', 'operation_status', 'created_at'),
|
||||
Index('uk_workspace_operations_request', 'request_id', unique=True),
|
||||
{'comment': '无状态 Backend 的 Workspace/Jupyter 异步操作记录'}
|
||||
)
|
||||
|
||||
operation_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
workspace_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
operation_type: Mapped[str] = mapped_column(String(24), nullable=False, comment='open/close/mount/unmount/start/stop/restart/recycle')
|
||||
operation_status: Mapped[str] = mapped_column(String(24), nullable=False, server_default=text("'pending'"), comment='pending/running/succeeded/failed/cancelled')
|
||||
state_version: Mapped[int] = mapped_column(INTEGER, nullable=False, server_default=text("0"), comment='乐观锁版本')
|
||||
requested_by: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3)'))
|
||||
runtime_id: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
request_id: Mapped[Optional[str]] = mapped_column(String(128))
|
||||
started_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
finished_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
error_code: Mapped[Optional[str]] = mapped_column(String(64))
|
||||
error_message: Mapped[Optional[str]] = mapped_column(Text)
|
||||
|
||||
users: Mapped['Users'] = relationship('Users', back_populates='workspace_operations')
|
||||
runtime: Mapped[Optional['RuntimeInstances']] = relationship('RuntimeInstances', back_populates='workspace_operations')
|
||||
workspace: Mapped['Workspaces'] = relationship('Workspaces', back_populates='workspace_operations')
|
||||
|
||||
|
||||
class NotebookSnapshots(Base):
|
||||
__tablename__ = 'notebook_snapshots'
|
||||
__table_args__ = (
|
||||
ForeignKeyConstraint(['artifact_object_id'], ['storage_objects.storage_object_id'], ondelete='RESTRICT', name='fk_snapshots_artifact_object'),
|
||||
ForeignKeyConstraint(['created_by'], ['users.user_id'], ondelete='RESTRICT', name='fk_snapshots_created_by'),
|
||||
ForeignKeyConstraint(['script_id'], ['scripts.script_id'], ondelete='RESTRICT', name='fk_snapshots_script'),
|
||||
ForeignKeyConstraint(['source_object_id'], ['storage_objects.storage_object_id'], ondelete='RESTRICT', name='fk_snapshots_source_object'),
|
||||
ForeignKeyConstraint(['workspace_id'], ['workspaces.workspace_id'], ondelete='RESTRICT', name='fk_snapshots_workspace'),
|
||||
Index('fk_snapshots_created_by', 'created_by'),
|
||||
Index('fk_snapshots_source_object', 'source_object_id'),
|
||||
Index('idx_snapshots_workspace_created', 'workspace_id', 'created_at'),
|
||||
Index('uk_snapshots_artifact', 'artifact_object_id', unique=True),
|
||||
Index('uk_snapshots_script_hash', 'script_id', 'content_hash', unique=True),
|
||||
{'comment': 'Notebook 开发快照,append-only'}
|
||||
)
|
||||
|
||||
snapshot_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
workspace_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
script_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
source_object_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
artifact_object_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
snapshot_name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
content_hash: Mapped[str] = mapped_column(CHAR(64), nullable=False)
|
||||
outputs_stripped: Mapped[int] = mapped_column(TINYINT(1), nullable=False, server_default=text("1"))
|
||||
created_by: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3)'))
|
||||
description: Mapped[Optional[str]] = mapped_column(String(1000))
|
||||
|
||||
artifact_object: Mapped['StorageObjects'] = relationship('StorageObjects', foreign_keys=[artifact_object_id], back_populates='notebook_snapshots')
|
||||
users: Mapped['Users'] = relationship('Users', back_populates='notebook_snapshots')
|
||||
script: Mapped['Scripts'] = relationship('Scripts', back_populates='notebook_snapshots')
|
||||
source_object: Mapped['StorageObjects'] = relationship('StorageObjects', foreign_keys=[source_object_id], back_populates='notebook_snapshots_')
|
||||
workspace: Mapped['Workspaces'] = relationship('Workspaces', back_populates='notebook_snapshots')
|
||||
|
||||
|
||||
class Versions(Base):
|
||||
__tablename__ = 'versions'
|
||||
__table_args__ = (
|
||||
ForeignKeyConstraint(['artifact_object_id'], ['storage_objects.storage_object_id'], ondelete='RESTRICT', name='fk_versions_artifact_object'),
|
||||
ForeignKeyConstraint(['created_by'], ['users.user_id'], ondelete='RESTRICT', name='fk_versions_created_by'),
|
||||
ForeignKeyConstraint(['script_id'], ['scripts.script_id'], ondelete='RESTRICT', name='fk_versions_script'),
|
||||
ForeignKeyConstraint(['source_object_id'], ['storage_objects.storage_object_id'], ondelete='RESTRICT', name='fk_versions_source_object'),
|
||||
ForeignKeyConstraint(['workspace_id'], ['workspaces.workspace_id'], ondelete='RESTRICT', name='fk_versions_workspace'),
|
||||
Index('fk_versions_source_object', 'source_object_id'),
|
||||
Index('idx_versions_creator', 'created_by', 'created_at'),
|
||||
Index('idx_versions_workspace_created', 'workspace_id', 'created_at'),
|
||||
Index('uk_versions_artifact', 'artifact_object_id', unique=True),
|
||||
Index('uk_versions_script_hash', 'script_id', 'content_hash', unique=True),
|
||||
Index('uk_versions_script_no', 'script_id', 'version_no', unique=True),
|
||||
{'comment': '不可变稳定版本;调度节点必须引用 versions_id'}
|
||||
)
|
||||
|
||||
versions_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True, comment='稳定版本唯一 ID')
|
||||
workspace_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
script_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
source_object_id: Mapped[str] = mapped_column(CHAR(26), nullable=False, comment='发布时的源对象')
|
||||
artifact_object_id: Mapped[str] = mapped_column(CHAR(26), nullable=False, comment='RustFS 不可变版本制品')
|
||||
version_no: Mapped[int] = mapped_column(INTEGER, nullable=False)
|
||||
version_label: Mapped[str] = mapped_column(String(32), nullable=False, comment='例如 v1.0')
|
||||
source_path: Mapped[str] = mapped_column(String(1024), nullable=False, comment='发布时路径快照')
|
||||
artifact_path: Mapped[str] = mapped_column(String(1500), nullable=False)
|
||||
content_hash: Mapped[str] = mapped_column(CHAR(64), nullable=False)
|
||||
file_size_bytes: Mapped[int] = mapped_column(BIGINT, nullable=False, server_default=text("0"))
|
||||
visibility: Mapped[str] = mapped_column(String(16), nullable=False, server_default=text("'private'"))
|
||||
created_by: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3)'))
|
||||
release_note: Mapped[Optional[str]] = mapped_column(String(1000))
|
||||
schedule_hidden_at: Mapped[Optional[datetime.datetime]] = mapped_column(
|
||||
DATETIME(fsp=3),
|
||||
comment='从调度稳定版本列表移除的时间;不影响版本和运行历史',
|
||||
)
|
||||
|
||||
artifact_object: Mapped['StorageObjects'] = relationship('StorageObjects', foreign_keys=[artifact_object_id], back_populates='versions')
|
||||
users: Mapped['Users'] = relationship('Users', back_populates='versions')
|
||||
script: Mapped['Scripts'] = relationship('Scripts', back_populates='versions')
|
||||
source_object: Mapped['StorageObjects'] = relationship('StorageObjects', foreign_keys=[source_object_id], back_populates='versions_')
|
||||
workspace: Mapped['Workspaces'] = relationship('Workspaces', back_populates='versions')
|
||||
experiments: Mapped[list['Experiments']] = relationship('Experiments', back_populates='versions')
|
||||
schedule_nodes: Mapped[list['ScheduleNodes']] = relationship('ScheduleNodes', back_populates='versions')
|
||||
schedule_node_runs: Mapped[list['ScheduleNodeRuns']] = relationship('ScheduleNodeRuns', back_populates='versions')
|
||||
|
||||
|
||||
class Experiments(Base):
|
||||
__tablename__ = 'experiments'
|
||||
__table_args__ = (
|
||||
ForeignKeyConstraint(['logs_object_id'], ['storage_objects.storage_object_id'], ondelete='SET NULL', name='fk_experiments_logs'),
|
||||
ForeignKeyConstraint(['owner_user_id'], ['users.user_id'], ondelete='RESTRICT', name='fk_experiments_owner'),
|
||||
ForeignKeyConstraint(['parent_experiment_id'], ['experiments.experiment_id'], ondelete='SET NULL', name='fk_experiments_parent'),
|
||||
ForeignKeyConstraint(['result_object_id'], ['storage_objects.storage_object_id'], ondelete='SET NULL', name='fk_experiments_result'),
|
||||
ForeignKeyConstraint(['schedule_run_id'], ['schedule_runs.run_id'], ondelete='SET NULL', name='fk_experiments_schedule_run'),
|
||||
ForeignKeyConstraint(['script_id'], ['scripts.script_id'], ondelete='SET NULL', name='fk_experiments_script'),
|
||||
ForeignKeyConstraint(['versions_id'], ['versions.versions_id'], ondelete='SET NULL', name='fk_experiments_version'),
|
||||
ForeignKeyConstraint(['workspace_id'], ['workspaces.workspace_id'], ondelete='RESTRICT', name='fk_experiments_workspace'),
|
||||
Index('fk_experiments_logs', 'logs_object_id'),
|
||||
Index('fk_experiments_parent', 'parent_experiment_id'),
|
||||
Index('fk_experiments_result', 'result_object_id'),
|
||||
Index('fk_experiments_script', 'script_id'),
|
||||
Index('idx_experiments_owner', 'owner_user_id', 'created_at'),
|
||||
Index('idx_experiments_schedule_run', 'schedule_run_id'),
|
||||
Index('idx_experiments_version', 'versions_id'),
|
||||
Index('idx_experiments_workspace', 'workspace_id', 'experiment_status', 'created_at'),
|
||||
{'comment': '实验记录'}
|
||||
)
|
||||
|
||||
experiment_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
workspace_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
owner_user_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
experiment_name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
source_type: Mapped[str] = mapped_column(String(24), nullable=False, comment='python/notebook/schedule/rerun')
|
||||
experiment_status: Mapped[str] = mapped_column(String(24), nullable=False, server_default=text("'queued'"))
|
||||
state_version: Mapped[int] = mapped_column(INTEGER, nullable=False, server_default=text("0"), comment='乐观锁版本')
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3)'))
|
||||
script_id: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
versions_id: Mapped[Optional[str]] = mapped_column(CHAR(26), comment='工作副本运行时可为空')
|
||||
schedule_run_id: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
parent_experiment_id: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
parameters_json: Mapped[Optional[dict]] = mapped_column(JSON)
|
||||
environment_json: Mapped[Optional[dict]] = mapped_column(JSON)
|
||||
result_summary: Mapped[Optional[str]] = mapped_column(String(2000))
|
||||
logs_object_id: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
result_object_id: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
started_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
finished_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
duration_ms: Mapped[Optional[int]] = mapped_column(BIGINT)
|
||||
deleted_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
|
||||
logs_object: Mapped[Optional['StorageObjects']] = relationship('StorageObjects', foreign_keys=[logs_object_id], back_populates='experiments')
|
||||
owner_user: Mapped['Users'] = relationship('Users', back_populates='experiments')
|
||||
parent_experiment: Mapped[Optional['Experiments']] = relationship('Experiments', remote_side=[experiment_id], back_populates='parent_experiment_reverse')
|
||||
parent_experiment_reverse: Mapped[list['Experiments']] = relationship('Experiments', remote_side=[parent_experiment_id], back_populates='parent_experiment')
|
||||
result_object: Mapped[Optional['StorageObjects']] = relationship('StorageObjects', foreign_keys=[result_object_id], back_populates='experiments_')
|
||||
schedule_run: Mapped[Optional['ScheduleRuns']] = relationship('ScheduleRuns', back_populates='experiments')
|
||||
script: Mapped[Optional['Scripts']] = relationship('Scripts', back_populates='experiments')
|
||||
versions: Mapped[Optional['Versions']] = relationship('Versions', back_populates='experiments')
|
||||
workspace: Mapped['Workspaces'] = relationship('Workspaces', back_populates='experiments')
|
||||
experiment_metrics: Mapped[list['ExperimentMetrics']] = relationship('ExperimentMetrics', back_populates='experiment')
|
||||
experiment_resources: Mapped[list['ExperimentResources']] = relationship('ExperimentResources', back_populates='experiment')
|
||||
|
||||
|
||||
class ScheduleNodes(Base):
|
||||
__tablename__ = 'schedule_nodes'
|
||||
__table_args__ = (
|
||||
ForeignKeyConstraint(['schedule_id'], ['schedules.schedule_id'], ondelete='CASCADE', name='fk_schedule_nodes_schedule'),
|
||||
ForeignKeyConstraint(['versions_id'], ['versions.versions_id'], ondelete='RESTRICT', name='fk_schedule_nodes_version'),
|
||||
Index('idx_schedule_nodes_version', 'versions_id'),
|
||||
Index('uk_schedule_nodes_key', 'schedule_id', 'node_key', unique=True),
|
||||
{'comment': 'DAG 节点,必须引用稳定版本'}
|
||||
)
|
||||
|
||||
node_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
schedule_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
node_key: Mapped[str] = mapped_column(String(64), nullable=False, comment='画布内稳定标识')
|
||||
node_name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
versions_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
timeout_seconds: Mapped[int] = mapped_column(INTEGER, nullable=False, server_default=text("600"))
|
||||
retry_count: Mapped[int] = mapped_column(INTEGER, nullable=False, server_default=text("0"))
|
||||
retry_interval_sec: Mapped[int] = mapped_column(INTEGER, nullable=False, server_default=text("5"))
|
||||
position_x: Mapped[decimal.Decimal] = mapped_column(DECIMAL(10, 2), nullable=False, server_default=text("0.00"))
|
||||
position_y: Mapped[decimal.Decimal] = mapped_column(DECIMAL(10, 2), nullable=False, server_default=text("0.00"))
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3)'))
|
||||
updated_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)'))
|
||||
arguments_json: Mapped[Optional[dict]] = mapped_column(JSON)
|
||||
env_refs_json: Mapped[Optional[dict]] = mapped_column(JSON, comment='只存密钥引用,不存明文密钥')
|
||||
|
||||
schedule: Mapped['Schedules'] = relationship('Schedules', back_populates='schedule_nodes')
|
||||
versions: Mapped['Versions'] = relationship('Versions', back_populates='schedule_nodes')
|
||||
schedule_edges: Mapped[list['ScheduleEdges']] = relationship('ScheduleEdges', foreign_keys='[ScheduleEdges.source_node_id]', back_populates='source_node')
|
||||
schedule_edges_: Mapped[list['ScheduleEdges']] = relationship('ScheduleEdges', foreign_keys='[ScheduleEdges.target_node_id]', back_populates='target_node')
|
||||
schedule_node_runs: Mapped[list['ScheduleNodeRuns']] = relationship('ScheduleNodeRuns', back_populates='node')
|
||||
|
||||
|
||||
class ExperimentMetrics(Base):
|
||||
__tablename__ = 'experiment_metrics'
|
||||
__table_args__ = (
|
||||
ForeignKeyConstraint(['experiment_id'], ['experiments.experiment_id'], ondelete='CASCADE', name='fk_experiment_metrics_experiment'),
|
||||
Index('idx_experiment_metrics_lookup', 'experiment_id', 'metric_name', 'step_no'),
|
||||
{'comment': '实验指标,支持筛选和曲线'}
|
||||
)
|
||||
|
||||
metric_id: Mapped[int] = mapped_column(BIGINT, primary_key=True)
|
||||
experiment_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
metric_name: Mapped[str] = mapped_column(String(128), nullable=False)
|
||||
recorded_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3)'))
|
||||
metric_value: Mapped[Optional[decimal.Decimal]] = mapped_column(Double(asdecimal=True))
|
||||
metric_text: Mapped[Optional[str]] = mapped_column(String(1000))
|
||||
step_no: Mapped[Optional[int]] = mapped_column(BigInteger)
|
||||
|
||||
experiment: Mapped['Experiments'] = relationship('Experiments', back_populates='experiment_metrics')
|
||||
|
||||
|
||||
class ExperimentResources(Base):
|
||||
__tablename__ = 'experiment_resources'
|
||||
__table_args__ = (
|
||||
ForeignKeyConstraint(['experiment_id'], ['experiments.experiment_id'], ondelete='CASCADE', name='fk_experiment_resources_experiment'),
|
||||
ForeignKeyConstraint(['resource_id'], ['data_resources.resource_id'], ondelete='RESTRICT', name='fk_experiment_resources_resource'),
|
||||
Index('fk_experiment_resources_resource', 'resource_id'),
|
||||
{'comment': '实验与数据资源'}
|
||||
)
|
||||
|
||||
experiment_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
resource_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
resource_role: Mapped[str] = mapped_column(String(16), primary_key=True, server_default=text("'input'"), comment='input/output')
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3)'))
|
||||
|
||||
experiment: Mapped['Experiments'] = relationship('Experiments', back_populates='experiment_resources')
|
||||
resource: Mapped['DataResources'] = relationship('DataResources', back_populates='experiment_resources')
|
||||
|
||||
|
||||
class ScheduleEdges(Base):
|
||||
__tablename__ = 'schedule_edges'
|
||||
__table_args__ = (
|
||||
ForeignKeyConstraint(['schedule_id'], ['schedules.schedule_id'], ondelete='CASCADE', name='fk_schedule_edges_schedule'),
|
||||
ForeignKeyConstraint(['source_node_id'], ['schedule_nodes.node_id'], ondelete='CASCADE', name='fk_schedule_edges_source'),
|
||||
ForeignKeyConstraint(['target_node_id'], ['schedule_nodes.node_id'], ondelete='CASCADE', name='fk_schedule_edges_target'),
|
||||
Index('fk_schedule_edges_source', 'source_node_id'),
|
||||
Index('idx_schedule_edges_target', 'target_node_id'),
|
||||
Index('uk_schedule_edges_pair', 'schedule_id', 'source_node_id', 'target_node_id', unique=True),
|
||||
{'comment': 'DAG 有向边'}
|
||||
)
|
||||
|
||||
edge_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
schedule_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
source_node_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
target_node_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3)'))
|
||||
condition_expr: Mapped[Optional[str]] = mapped_column(String(1000))
|
||||
|
||||
schedule: Mapped['Schedules'] = relationship('Schedules', back_populates='schedule_edges')
|
||||
source_node: Mapped['ScheduleNodes'] = relationship('ScheduleNodes', foreign_keys=[source_node_id], back_populates='schedule_edges')
|
||||
target_node: Mapped['ScheduleNodes'] = relationship('ScheduleNodes', foreign_keys=[target_node_id], back_populates='schedule_edges_')
|
||||
|
||||
|
||||
class ScheduleNodeRuns(Base):
|
||||
__tablename__ = 'schedule_node_runs'
|
||||
__table_args__ = (
|
||||
ForeignKeyConstraint(['logs_object_id'], ['storage_objects.storage_object_id'], ondelete='SET NULL', name='fk_node_runs_logs'),
|
||||
ForeignKeyConstraint(['node_id'], ['schedule_nodes.node_id'], ondelete='RESTRICT', name='fk_node_runs_node'),
|
||||
ForeignKeyConstraint(['result_object_id'], ['storage_objects.storage_object_id'], ondelete='SET NULL', name='fk_node_runs_result'),
|
||||
ForeignKeyConstraint(['run_id'], ['schedule_runs.run_id'], ondelete='CASCADE', name='fk_node_runs_run'),
|
||||
ForeignKeyConstraint(['versions_id'], ['versions.versions_id'], ondelete='RESTRICT', name='fk_node_runs_version'),
|
||||
Index('fk_node_runs_logs', 'logs_object_id'),
|
||||
Index('fk_node_runs_node', 'node_id'),
|
||||
Index('fk_node_runs_result', 'result_object_id'),
|
||||
Index('idx_node_runs_status', 'run_id', 'node_status'),
|
||||
Index('idx_node_runs_version', 'versions_id'),
|
||||
Index('uk_node_runs_attempt', 'run_id', 'node_id', 'attempt_no', unique=True),
|
||||
{'comment': '调度节点运行与重试'}
|
||||
)
|
||||
|
||||
node_run_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
run_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
node_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
versions_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
attempt_no: Mapped[int] = mapped_column(INTEGER, nullable=False, server_default=text("1"))
|
||||
node_status: Mapped[str] = mapped_column(String(24), nullable=False, server_default=text("'queued'"), comment='queued/running/succeeded/failed/skipped/cancelled/timed_out')
|
||||
state_version: Mapped[int] = mapped_column(INTEGER, nullable=False, server_default=text("0"), comment='乐观锁版本')
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False, server_default=text('CURRENT_TIMESTAMP(3)'))
|
||||
started_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
finished_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
duration_ms: Mapped[Optional[int]] = mapped_column(BIGINT)
|
||||
exit_code: Mapped[Optional[int]] = mapped_column(Integer)
|
||||
message: Mapped[Optional[str]] = mapped_column(String(2000))
|
||||
metrics_json: Mapped[Optional[dict]] = mapped_column(JSON)
|
||||
logs_object_id: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
result_object_id: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
|
||||
logs_object: Mapped[Optional['StorageObjects']] = relationship('StorageObjects', foreign_keys=[logs_object_id], back_populates='schedule_node_runs')
|
||||
node: Mapped['ScheduleNodes'] = relationship('ScheduleNodes', back_populates='schedule_node_runs')
|
||||
result_object: Mapped[Optional['StorageObjects']] = relationship('StorageObjects', foreign_keys=[result_object_id], back_populates='schedule_node_runs_')
|
||||
run: Mapped['ScheduleRuns'] = relationship('ScheduleRuns', back_populates='schedule_node_runs')
|
||||
versions: Mapped['Versions'] = relationship('Versions', back_populates='schedule_node_runs')
|
||||
@@ -1,5 +1,50 @@
|
||||
# coding=utf-8
|
||||
"""
|
||||
@Time :2026/7/29
|
||||
@Author :tao.chen
|
||||
"""
|
||||
from common.db.base import Base
|
||||
from common.db.models.audit import AuditLogs
|
||||
from common.db.models.events import ConsumerInbox, OutboxEvents
|
||||
from common.db.models.experiments import (
|
||||
ExperimentMetrics,
|
||||
ExperimentResources,
|
||||
Experiments,
|
||||
)
|
||||
from common.db.models.identity import Permissions, RolePermissions, Roles, Users
|
||||
from common.db.models.runtime import EditSessions, RuntimeInstances, WorkspaceOperations
|
||||
from common.db.models.schedules import (
|
||||
ScheduleEdges,
|
||||
ScheduleNodeRuns,
|
||||
ScheduleNodes,
|
||||
ScheduleRuns,
|
||||
Schedules,
|
||||
)
|
||||
from common.db.models.scripts import NotebookSnapshots, Scripts, Versions
|
||||
from common.db.models.storage import DataResources, StorageObjects, UploadSessions
|
||||
from common.db.models.workspaces import WorkspaceMembers, Workspaces
|
||||
|
||||
__all__ = [
|
||||
"Base",
|
||||
"AuditLogs",
|
||||
"ConsumerInbox",
|
||||
"DataResources",
|
||||
"EditSessions",
|
||||
"ExperimentMetrics",
|
||||
"ExperimentResources",
|
||||
"Experiments",
|
||||
"NotebookSnapshots",
|
||||
"OutboxEvents",
|
||||
"Permissions",
|
||||
"RolePermissions",
|
||||
"Roles",
|
||||
"RuntimeInstances",
|
||||
"ScheduleEdges",
|
||||
"ScheduleNodeRuns",
|
||||
"ScheduleNodes",
|
||||
"ScheduleRuns",
|
||||
"Schedules",
|
||||
"Scripts",
|
||||
"StorageObjects",
|
||||
"UploadSessions",
|
||||
"Users",
|
||||
"Versions",
|
||||
"WorkspaceMembers",
|
||||
"WorkspaceOperations",
|
||||
"Workspaces",
|
||||
]
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import datetime
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy import Index, JSON, String, text
|
||||
from sqlalchemy.dialects.mysql import BIGINT, CHAR, DATETIME, TINYINT
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from common.db.base import Base
|
||||
|
||||
|
||||
class AuditLogs(Base):
|
||||
__tablename__ = "audit_logs"
|
||||
__table_args__ = (
|
||||
Index("idx_audit_action_time", "action_code", "created_at"),
|
||||
Index("idx_audit_actor_time", "actor_user_id", "created_at"),
|
||||
Index("idx_audit_workspace_time", "workspace_id", "created_at"),
|
||||
{"comment": "操作审计日志"},
|
||||
)
|
||||
|
||||
audit_id: Mapped[int] = mapped_column(BIGINT, primary_key=True)
|
||||
action_code: Mapped[str] = mapped_column(String(128), nullable=False)
|
||||
target_type: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
operation_status: Mapped[str] = mapped_column(
|
||||
String(16), nullable=False, server_default=text("'success'")
|
||||
)
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3), nullable=False, server_default=text("CURRENT_TIMESTAMP(3)")
|
||||
)
|
||||
workspace_id: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
actor_user_id: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
target_id: Mapped[Optional[str]] = mapped_column(String(128))
|
||||
client_ip: Mapped[Optional[str]] = mapped_column(String(45))
|
||||
user_agent: Mapped[Optional[str]] = mapped_column(String(1000))
|
||||
detail_json: Mapped[Optional[dict]] = mapped_column(JSON)
|
||||
is_deleted: Mapped[int] = mapped_column(
|
||||
TINYINT(1), nullable=False, server_default=text("0")
|
||||
)
|
||||
deleted_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
@@ -0,0 +1,79 @@
|
||||
import datetime
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy import Index, JSON, String, text
|
||||
from sqlalchemy.dialects.mysql import CHAR, DATETIME, INTEGER, SMALLINT, TINYINT
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from common.db.base import Base
|
||||
|
||||
|
||||
class ConsumerInbox(Base):
|
||||
__tablename__ = "consumer_inbox"
|
||||
__table_args__ = (
|
||||
Index("idx_consumer_inbox_status", "consumer_name", "process_status", "created_at"),
|
||||
{"comment": "消费者幂等 Inbox,防止 Stream 重投导致重复执行"},
|
||||
)
|
||||
|
||||
consumer_name: Mapped[str] = mapped_column(String(128), primary_key=True)
|
||||
event_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
process_status: Mapped[str] = mapped_column(
|
||||
String(16),
|
||||
nullable=False,
|
||||
server_default=text("'processing'"),
|
||||
comment="processing/succeeded/failed",
|
||||
)
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3), nullable=False, server_default=text("CURRENT_TIMESTAMP(3)")
|
||||
)
|
||||
message_id: Mapped[Optional[str]] = mapped_column(
|
||||
String(128), comment="Inbox message ID"
|
||||
)
|
||||
processed_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
error_message: Mapped[Optional[str]] = mapped_column(String(2000))
|
||||
is_deleted: Mapped[int] = mapped_column(
|
||||
TINYINT(1), nullable=False, server_default=text("0")
|
||||
)
|
||||
deleted_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
|
||||
|
||||
class OutboxEvents(Base):
|
||||
__tablename__ = "outbox_events"
|
||||
__table_args__ = (
|
||||
Index("idx_outbox_aggregate", "aggregate_type", "aggregate_id", "created_at"),
|
||||
Index("idx_outbox_idempotency", "idempotency_key"),
|
||||
Index("idx_outbox_pending", "event_status", "available_at", "created_at"),
|
||||
{"comment": "事务 Outbox;提交后发布到内部事件总线"},
|
||||
)
|
||||
|
||||
event_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
aggregate_type: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
aggregate_id: Mapped[str] = mapped_column(String(128), nullable=False)
|
||||
event_type: Mapped[str] = mapped_column(String(128), nullable=False)
|
||||
schema_version: Mapped[int] = mapped_column(
|
||||
SMALLINT, nullable=False, server_default=text("1")
|
||||
)
|
||||
payload_json: Mapped[dict] = mapped_column(JSON, nullable=False)
|
||||
event_status: Mapped[str] = mapped_column(
|
||||
String(16),
|
||||
nullable=False,
|
||||
server_default=text("'pending'"),
|
||||
comment="pending/published/failed",
|
||||
)
|
||||
available_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3), nullable=False, server_default=text("CURRENT_TIMESTAMP(3)")
|
||||
)
|
||||
retry_count: Mapped[int] = mapped_column(
|
||||
INTEGER, nullable=False, server_default=text("0")
|
||||
)
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3), nullable=False, server_default=text("CURRENT_TIMESTAMP(3)")
|
||||
)
|
||||
trace_id: Mapped[Optional[str]] = mapped_column(String(64))
|
||||
idempotency_key: Mapped[Optional[str]] = mapped_column(String(128))
|
||||
published_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
last_error: Mapped[Optional[str]] = mapped_column(String(2000))
|
||||
is_deleted: Mapped[int] = mapped_column(
|
||||
TINYINT(1), nullable=False, server_default=text("0")
|
||||
)
|
||||
deleted_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
@@ -0,0 +1,112 @@
|
||||
import datetime
|
||||
import decimal
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy import BigInteger, Double, Index, JSON, String, text
|
||||
from sqlalchemy.dialects.mysql import BIGINT, CHAR, DATETIME, INTEGER, TINYINT
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from common.db.base import Base
|
||||
|
||||
|
||||
class Experiments(Base):
|
||||
__tablename__ = "experiments"
|
||||
__table_args__ = (
|
||||
Index("fk_experiments_logs", "logs_object_id"),
|
||||
Index("fk_experiments_parent", "parent_experiment_id"),
|
||||
Index("fk_experiments_result", "result_object_id"),
|
||||
Index("fk_experiments_script", "script_id"),
|
||||
Index("idx_experiments_owner", "owner_user_id", "created_at"),
|
||||
Index("idx_experiments_schedule_run", "schedule_run_id"),
|
||||
Index("idx_experiments_version", "versions_id"),
|
||||
Index(
|
||||
"idx_experiments_workspace",
|
||||
"workspace_id",
|
||||
"experiment_status",
|
||||
"created_at",
|
||||
),
|
||||
{"comment": "实验记录"},
|
||||
)
|
||||
|
||||
experiment_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
workspace_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
owner_user_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
experiment_name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
source_type: Mapped[str] = mapped_column(
|
||||
String(24), nullable=False, comment="python/notebook/schedule/rerun"
|
||||
)
|
||||
experiment_status: Mapped[str] = mapped_column(
|
||||
String(24), nullable=False, server_default=text("'queued'")
|
||||
)
|
||||
state_version: Mapped[int] = mapped_column(
|
||||
INTEGER, nullable=False, server_default=text("0"), comment="乐观锁版本"
|
||||
)
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3), nullable=False, server_default=text("CURRENT_TIMESTAMP(3)")
|
||||
)
|
||||
script_id: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
versions_id: Mapped[Optional[str]] = mapped_column(
|
||||
CHAR(26), comment="工作副本运行时可为空"
|
||||
)
|
||||
schedule_run_id: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
parent_experiment_id: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
parameters_json: Mapped[Optional[dict]] = mapped_column(JSON)
|
||||
environment_json: Mapped[Optional[dict]] = mapped_column(JSON)
|
||||
result_summary: Mapped[Optional[str]] = mapped_column(String(2000))
|
||||
logs_object_id: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
result_object_id: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
started_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
finished_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
duration_ms: Mapped[Optional[int]] = mapped_column(BIGINT)
|
||||
is_deleted: Mapped[int] = mapped_column(
|
||||
TINYINT(1), nullable=False, server_default=text("0")
|
||||
)
|
||||
deleted_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
|
||||
|
||||
class ExperimentMetrics(Base):
|
||||
__tablename__ = "experiment_metrics"
|
||||
__table_args__ = (
|
||||
Index("idx_experiment_metrics_lookup", "experiment_id", "metric_name", "step_no"),
|
||||
{"comment": "实验指标,支持筛选和曲线"},
|
||||
)
|
||||
|
||||
metric_id: Mapped[int] = mapped_column(BIGINT, primary_key=True)
|
||||
experiment_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
metric_name: Mapped[str] = mapped_column(String(128), nullable=False)
|
||||
recorded_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3), nullable=False, server_default=text("CURRENT_TIMESTAMP(3)")
|
||||
)
|
||||
metric_value: Mapped[Optional[decimal.Decimal]] = mapped_column(
|
||||
Double(asdecimal=True)
|
||||
)
|
||||
metric_text: Mapped[Optional[str]] = mapped_column(String(1000))
|
||||
step_no: Mapped[Optional[int]] = mapped_column(BigInteger)
|
||||
is_deleted: Mapped[int] = mapped_column(
|
||||
TINYINT(1), nullable=False, server_default=text("0")
|
||||
)
|
||||
deleted_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
|
||||
|
||||
class ExperimentResources(Base):
|
||||
__tablename__ = "experiment_resources"
|
||||
__table_args__ = (
|
||||
Index("fk_experiment_resources_resource", "resource_id"),
|
||||
{"comment": "实验与数据资源"},
|
||||
)
|
||||
|
||||
experiment_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
resource_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
resource_role: Mapped[str] = mapped_column(
|
||||
String(16),
|
||||
primary_key=True,
|
||||
server_default=text("'input'"),
|
||||
comment="input/output",
|
||||
)
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3), nullable=False, server_default=text("CURRENT_TIMESTAMP(3)")
|
||||
)
|
||||
is_deleted: Mapped[int] = mapped_column(
|
||||
TINYINT(1), nullable=False, server_default=text("0")
|
||||
)
|
||||
deleted_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
@@ -0,0 +1,117 @@
|
||||
import datetime
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy import Index, String, text
|
||||
from sqlalchemy.dialects.mysql import CHAR, DATETIME, TINYINT
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from common.db.base import Base
|
||||
|
||||
|
||||
class Permissions(Base):
|
||||
__tablename__ = "permissions"
|
||||
__table_args__ = (
|
||||
Index("idx_permissions_module", "module_code"),
|
||||
Index("uk_permissions_code", "permission_code", unique=True),
|
||||
{"comment": "权限点"},
|
||||
)
|
||||
|
||||
permission_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
permission_code: Mapped[str] = mapped_column(String(128), nullable=False)
|
||||
permission_name: Mapped[str] = mapped_column(String(100), nullable=False)
|
||||
module_code: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3), nullable=False, server_default=text("CURRENT_TIMESTAMP(3)")
|
||||
)
|
||||
description: Mapped[Optional[str]] = mapped_column(String(500))
|
||||
is_deleted: Mapped[int] = mapped_column(
|
||||
TINYINT(1), nullable=False, server_default=text("0")
|
||||
)
|
||||
deleted_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
|
||||
|
||||
class Roles(Base):
|
||||
__tablename__ = "roles"
|
||||
__table_args__ = (
|
||||
Index("uk_roles_code", "role_code", unique=True),
|
||||
{"comment": "角色"},
|
||||
)
|
||||
|
||||
role_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
role_code: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
role_name: Mapped[str] = mapped_column(String(100), nullable=False)
|
||||
role_scope: Mapped[str] = mapped_column(
|
||||
String(16), nullable=False, comment="platform/workspace"
|
||||
)
|
||||
is_builtin: Mapped[int] = mapped_column(
|
||||
TINYINT(1), nullable=False, server_default=text("0")
|
||||
)
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3), nullable=False, server_default=text("CURRENT_TIMESTAMP(3)")
|
||||
)
|
||||
updated_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3),
|
||||
nullable=False,
|
||||
server_default=text("CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"),
|
||||
)
|
||||
description: Mapped[Optional[str]] = mapped_column(String(500))
|
||||
is_deleted: Mapped[int] = mapped_column(
|
||||
TINYINT(1), nullable=False, server_default=text("0")
|
||||
)
|
||||
deleted_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
|
||||
|
||||
class RolePermissions(Base):
|
||||
__tablename__ = "role_permissions"
|
||||
__table_args__ = (
|
||||
Index("fk_role_permissions_permission", "permission_id"),
|
||||
{"comment": "角色权限"},
|
||||
)
|
||||
|
||||
role_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
permission_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3), nullable=False, server_default=text("CURRENT_TIMESTAMP(3)")
|
||||
)
|
||||
is_deleted: Mapped[int] = mapped_column(
|
||||
TINYINT(1), nullable=False, server_default=text("0")
|
||||
)
|
||||
deleted_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
|
||||
|
||||
class Users(Base):
|
||||
__tablename__ = "users"
|
||||
__table_args__ = (
|
||||
Index("fk_users_platform_role", "platform_role_id"),
|
||||
Index("idx_users_status", "status"),
|
||||
Index("uk_users_email", "email", unique=True),
|
||||
Index("uk_users_username", "username", unique=True),
|
||||
{"comment": "平台用户"},
|
||||
)
|
||||
|
||||
user_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
username: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
display_name: Mapped[str] = mapped_column(String(100), nullable=False)
|
||||
password_hash: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
status: Mapped[str] = mapped_column(
|
||||
String(16),
|
||||
nullable=False,
|
||||
server_default=text("'active'"),
|
||||
comment="active/disabled/locked",
|
||||
)
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3), nullable=False, server_default=text("CURRENT_TIMESTAMP(3)")
|
||||
)
|
||||
updated_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3),
|
||||
nullable=False,
|
||||
server_default=text("CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"),
|
||||
)
|
||||
email: Mapped[Optional[str]] = mapped_column(String(255))
|
||||
platform_role_id: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
avatar_uri: Mapped[Optional[str]] = mapped_column(String(1000))
|
||||
last_login_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
is_deleted: Mapped[int] = mapped_column(
|
||||
TINYINT(1), nullable=False, server_default=text("0")
|
||||
)
|
||||
deleted_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
@@ -0,0 +1,168 @@
|
||||
import datetime
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy import BINARY, Index, String, Text, text
|
||||
from sqlalchemy.dialects.mysql import CHAR, DATETIME, INTEGER, TINYINT
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from common.db.base import Base
|
||||
|
||||
|
||||
class RuntimeInstances(Base):
|
||||
__tablename__ = "runtime_instances"
|
||||
__table_args__ = (
|
||||
Index("fk_runtime_started_by", "started_by"),
|
||||
Index("idx_runtime_lease", "actual_state", "lease_expires_at"),
|
||||
Index("idx_runtime_owner_state", "owner_user_id", "actual_state"),
|
||||
Index(
|
||||
"idx_runtime_workspace_state",
|
||||
"workspace_id",
|
||||
"runtime_type",
|
||||
"actual_state",
|
||||
),
|
||||
{"comment": "Jupyter/未来 VS Code、OpenCode Runtime 实例"},
|
||||
)
|
||||
|
||||
runtime_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
workspace_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
runtime_type: Mapped[str] = mapped_column(
|
||||
String(24), nullable=False, server_default=text("'jupyter'")
|
||||
)
|
||||
runtime_provider: Mapped[str] = mapped_column(
|
||||
String(24), nullable=False, comment="process/docker/kubernetes"
|
||||
)
|
||||
proxy_base_path: Mapped[str] = mapped_column(String(512), nullable=False)
|
||||
desired_state: Mapped[str] = mapped_column(
|
||||
String(16), nullable=False, server_default=text("'running'")
|
||||
)
|
||||
actual_state: Mapped[str] = mapped_column(
|
||||
String(24),
|
||||
nullable=False,
|
||||
server_default=text("'provisioning'"),
|
||||
comment="provisioning/starting/running/unhealthy/stopping/stopped/failed",
|
||||
)
|
||||
state_version: Mapped[int] = mapped_column(
|
||||
INTEGER, nullable=False, server_default=text("0"), comment="乐观锁版本"
|
||||
)
|
||||
started_by: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3), nullable=False, server_default=text("CURRENT_TIMESTAMP(3)")
|
||||
)
|
||||
updated_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3),
|
||||
nullable=False,
|
||||
server_default=text("CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"),
|
||||
)
|
||||
owner_user_id: Mapped[Optional[str]] = mapped_column(
|
||||
CHAR(26), comment="为空表示 Workspace 级 Runtime"
|
||||
)
|
||||
runtime_ref: Mapped[Optional[str]] = mapped_column(
|
||||
String(255), comment="PID/container ID/pod UID"
|
||||
)
|
||||
host_node: Mapped[Optional[str]] = mapped_column(String(255))
|
||||
internal_url: Mapped[Optional[str]] = mapped_column(String(1000))
|
||||
started_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
last_heartbeat_at: Mapped[Optional[datetime.datetime]] = mapped_column(
|
||||
DATETIME(fsp=3)
|
||||
)
|
||||
lease_expires_at: Mapped[Optional[datetime.datetime]] = mapped_column(
|
||||
DATETIME(fsp=3)
|
||||
)
|
||||
stopped_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
error_message: Mapped[Optional[str]] = mapped_column(Text)
|
||||
is_deleted: Mapped[int] = mapped_column(
|
||||
TINYINT(1), nullable=False, server_default=text("0")
|
||||
)
|
||||
deleted_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
|
||||
|
||||
class EditSessions(Base):
|
||||
__tablename__ = "edit_sessions"
|
||||
__table_args__ = (
|
||||
Index("fk_edit_sessions_workspace", "workspace_id"),
|
||||
Index(
|
||||
"idx_edit_sessions_object",
|
||||
"storage_object_id",
|
||||
"session_status",
|
||||
"expires_at",
|
||||
),
|
||||
Index("idx_edit_sessions_runtime", "runtime_id", "session_status"),
|
||||
Index("idx_edit_sessions_user", "user_id", "session_status"),
|
||||
{"comment": "编辑会话审计"},
|
||||
)
|
||||
|
||||
edit_session_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
workspace_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
storage_object_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
user_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
lock_token_hash: Mapped[bytes] = mapped_column(
|
||||
BINARY(32), nullable=False, comment="不保存原始 token"
|
||||
)
|
||||
session_status: Mapped[str] = mapped_column(
|
||||
String(16),
|
||||
nullable=False,
|
||||
server_default=text("'active'"),
|
||||
comment="active/closed/expired/failed",
|
||||
)
|
||||
started_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3), nullable=False, server_default=text("CURRENT_TIMESTAMP(3)")
|
||||
)
|
||||
last_heartbeat_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3), nullable=False, server_default=text("CURRENT_TIMESTAMP(3)")
|
||||
)
|
||||
expires_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False)
|
||||
runtime_id: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
jupyter_session_id: Mapped[Optional[str]] = mapped_column(String(255))
|
||||
ended_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
end_reason: Mapped[Optional[str]] = mapped_column(String(64))
|
||||
is_deleted: Mapped[int] = mapped_column(
|
||||
TINYINT(1), nullable=False, server_default=text("0")
|
||||
)
|
||||
deleted_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
|
||||
|
||||
class WorkspaceOperations(Base):
|
||||
__tablename__ = "workspace_operations"
|
||||
__table_args__ = (
|
||||
Index("fk_workspace_operations_user", "requested_by"),
|
||||
Index("idx_workspace_operations_runtime", "runtime_id", "created_at"),
|
||||
Index(
|
||||
"idx_workspace_operations_workspace",
|
||||
"workspace_id",
|
||||
"operation_status",
|
||||
"created_at",
|
||||
),
|
||||
Index("uk_workspace_operations_request", "request_id", unique=True),
|
||||
{"comment": "无状态 Backend 的 Workspace/Jupyter 异步操作记录"},
|
||||
)
|
||||
|
||||
operation_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
workspace_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
operation_type: Mapped[str] = mapped_column(
|
||||
String(24),
|
||||
nullable=False,
|
||||
comment="open/close/mount/unmount/start/stop/restart/recycle",
|
||||
)
|
||||
operation_status: Mapped[str] = mapped_column(
|
||||
String(24),
|
||||
nullable=False,
|
||||
server_default=text("'pending'"),
|
||||
comment="pending/running/succeeded/failed/cancelled",
|
||||
)
|
||||
state_version: Mapped[int] = mapped_column(
|
||||
INTEGER, nullable=False, server_default=text("0"), comment="乐观锁版本"
|
||||
)
|
||||
requested_by: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3), nullable=False, server_default=text("CURRENT_TIMESTAMP(3)")
|
||||
)
|
||||
runtime_id: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
request_id: Mapped[Optional[str]] = mapped_column(String(128))
|
||||
started_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
finished_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
error_code: Mapped[Optional[str]] = mapped_column(String(64))
|
||||
error_message: Mapped[Optional[str]] = mapped_column(Text)
|
||||
is_deleted: Mapped[int] = mapped_column(
|
||||
TINYINT(1), nullable=False, server_default=text("0")
|
||||
)
|
||||
deleted_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
@@ -0,0 +1,245 @@
|
||||
import datetime
|
||||
import decimal
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy import DECIMAL, Index, Integer, JSON, String, Text, text
|
||||
from sqlalchemy.dialects.mysql import BIGINT, CHAR, DATETIME, INTEGER, TINYINT
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from common.db.base import Base
|
||||
|
||||
|
||||
class Schedules(Base):
|
||||
__tablename__ = "schedules"
|
||||
__table_args__ = (
|
||||
Index("fk_schedules_created_by", "created_by"),
|
||||
Index("fk_schedules_updated_by", "updated_by"),
|
||||
Index("idx_schedules_due", "enabled", "next_run_at"),
|
||||
Index("idx_schedules_workspace", "workspace_id", "enabled", "updated_at"),
|
||||
{"comment": "调度方案"},
|
||||
)
|
||||
|
||||
schedule_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
workspace_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
schedule_name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
trigger_type: Mapped[str] = mapped_column(
|
||||
String(16),
|
||||
nullable=False,
|
||||
server_default=text("'cron'"),
|
||||
comment="manual/cron/api",
|
||||
)
|
||||
timezone: Mapped[str] = mapped_column(
|
||||
String(64), nullable=False, server_default=text("'Asia/Shanghai'")
|
||||
)
|
||||
enabled: Mapped[int] = mapped_column(
|
||||
TINYINT(1), nullable=False, server_default=text("0")
|
||||
)
|
||||
workflow_version: Mapped[int] = mapped_column(
|
||||
INTEGER, nullable=False, server_default=text("1")
|
||||
)
|
||||
max_concurrency: Mapped[int] = mapped_column(
|
||||
INTEGER, nullable=False, server_default=text("1")
|
||||
)
|
||||
failure_policy: Mapped[str] = mapped_column(
|
||||
String(24), nullable=False, server_default=text("'stop'"), comment="stop/continue"
|
||||
)
|
||||
created_by: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
updated_by: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3), nullable=False, server_default=text("CURRENT_TIMESTAMP(3)")
|
||||
)
|
||||
updated_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3),
|
||||
nullable=False,
|
||||
server_default=text("CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"),
|
||||
)
|
||||
description: Mapped[Optional[str]] = mapped_column(String(1000))
|
||||
cron_expression: Mapped[Optional[str]] = mapped_column(String(128))
|
||||
last_run_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
next_run_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
is_deleted: Mapped[int] = mapped_column(
|
||||
TINYINT(1), nullable=False, server_default=text("0")
|
||||
)
|
||||
deleted_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
|
||||
|
||||
class ScheduleRuns(Base):
|
||||
__tablename__ = "schedule_runs"
|
||||
__table_args__ = (
|
||||
Index("fk_schedule_runs_logs", "logs_object_id"),
|
||||
Index("fk_schedule_runs_result", "result_object_id"),
|
||||
Index("fk_schedule_runs_user", "triggered_by"),
|
||||
Index("idx_schedule_runs_schedule", "schedule_id", "created_at"),
|
||||
Index("idx_schedule_runs_status", "run_status", "queued_at"),
|
||||
Index(
|
||||
"idx_schedule_runs_workspace_status",
|
||||
"workspace_id",
|
||||
"run_status",
|
||||
"queued_at",
|
||||
),
|
||||
Index("uk_schedule_runs_idempotency", "idempotency_key", unique=True),
|
||||
{"comment": "调度运行"},
|
||||
)
|
||||
|
||||
run_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
schedule_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
workspace_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
workflow_version: Mapped[int] = mapped_column(INTEGER, nullable=False)
|
||||
trigger_type: Mapped[str] = mapped_column(
|
||||
String(16), nullable=False, comment="manual/cron/api/retry"
|
||||
)
|
||||
idempotency_key: Mapped[str] = mapped_column(String(128), nullable=False)
|
||||
run_status: Mapped[str] = mapped_column(
|
||||
String(24),
|
||||
nullable=False,
|
||||
server_default=text("'queued'"),
|
||||
comment="queued/running/succeeded/failed/cancelled/timed_out",
|
||||
)
|
||||
state_version: Mapped[int] = mapped_column(
|
||||
INTEGER, nullable=False, server_default=text("0"), comment="乐观锁版本"
|
||||
)
|
||||
schedule_snapshot: Mapped[dict] = mapped_column(
|
||||
JSON, nullable=False, comment="执行时 DAG 快照"
|
||||
)
|
||||
queued_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3), nullable=False, server_default=text("CURRENT_TIMESTAMP(3)")
|
||||
)
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3), nullable=False, server_default=text("CURRENT_TIMESTAMP(3)")
|
||||
)
|
||||
triggered_by: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
started_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
finished_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
duration_ms: Mapped[Optional[int]] = mapped_column(BIGINT)
|
||||
error_code: Mapped[Optional[str]] = mapped_column(String(64))
|
||||
error_message: Mapped[Optional[str]] = mapped_column(Text)
|
||||
logs_object_id: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
result_object_id: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
is_deleted: Mapped[int] = mapped_column(
|
||||
TINYINT(1), nullable=False, server_default=text("0")
|
||||
)
|
||||
deleted_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
|
||||
|
||||
class ScheduleNodes(Base):
|
||||
__tablename__ = "schedule_nodes"
|
||||
__table_args__ = (
|
||||
Index("idx_schedule_nodes_version", "versions_id"),
|
||||
Index("uk_schedule_nodes_key", "schedule_id", "node_key", unique=True),
|
||||
{"comment": "DAG 节点,必须引用稳定版本"},
|
||||
)
|
||||
|
||||
node_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
schedule_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
node_key: Mapped[str] = mapped_column(
|
||||
String(64), nullable=False, comment="画布内稳定标识"
|
||||
)
|
||||
node_name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
versions_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
timeout_seconds: Mapped[int] = mapped_column(
|
||||
INTEGER, nullable=False, server_default=text("600")
|
||||
)
|
||||
retry_count: Mapped[int] = mapped_column(
|
||||
INTEGER, nullable=False, server_default=text("0")
|
||||
)
|
||||
retry_interval_sec: Mapped[int] = mapped_column(
|
||||
INTEGER, nullable=False, server_default=text("5")
|
||||
)
|
||||
position_x: Mapped[decimal.Decimal] = mapped_column(
|
||||
DECIMAL(10, 2), nullable=False, server_default=text("0.00")
|
||||
)
|
||||
position_y: Mapped[decimal.Decimal] = mapped_column(
|
||||
DECIMAL(10, 2), nullable=False, server_default=text("0.00")
|
||||
)
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3), nullable=False, server_default=text("CURRENT_TIMESTAMP(3)")
|
||||
)
|
||||
updated_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3),
|
||||
nullable=False,
|
||||
server_default=text("CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"),
|
||||
)
|
||||
arguments_json: Mapped[Optional[dict]] = mapped_column(JSON)
|
||||
env_refs_json: Mapped[Optional[dict]] = mapped_column(
|
||||
JSON, comment="只存密钥引用,不存明文密钥"
|
||||
)
|
||||
is_deleted: Mapped[int] = mapped_column(
|
||||
TINYINT(1), nullable=False, server_default=text("0")
|
||||
)
|
||||
deleted_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
|
||||
|
||||
class ScheduleEdges(Base):
|
||||
__tablename__ = "schedule_edges"
|
||||
__table_args__ = (
|
||||
Index("fk_schedule_edges_source", "source_node_id"),
|
||||
Index("idx_schedule_edges_target", "target_node_id"),
|
||||
Index(
|
||||
"uk_schedule_edges_pair",
|
||||
"schedule_id",
|
||||
"source_node_id",
|
||||
"target_node_id",
|
||||
unique=True,
|
||||
),
|
||||
{"comment": "DAG 有向边"},
|
||||
)
|
||||
|
||||
edge_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
schedule_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
source_node_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
target_node_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3), nullable=False, server_default=text("CURRENT_TIMESTAMP(3)")
|
||||
)
|
||||
condition_expr: Mapped[Optional[str]] = mapped_column(String(1000))
|
||||
is_deleted: Mapped[int] = mapped_column(
|
||||
TINYINT(1), nullable=False, server_default=text("0")
|
||||
)
|
||||
deleted_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
|
||||
|
||||
class ScheduleNodeRuns(Base):
|
||||
__tablename__ = "schedule_node_runs"
|
||||
__table_args__ = (
|
||||
Index("fk_node_runs_logs", "logs_object_id"),
|
||||
Index("fk_node_runs_node", "node_id"),
|
||||
Index("fk_node_runs_result", "result_object_id"),
|
||||
Index("idx_node_runs_status", "run_id", "node_status"),
|
||||
Index("idx_node_runs_version", "versions_id"),
|
||||
Index(
|
||||
"uk_node_runs_attempt", "run_id", "node_id", "attempt_no", unique=True
|
||||
),
|
||||
{"comment": "调度节点运行与重试"},
|
||||
)
|
||||
|
||||
node_run_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
run_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
node_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
versions_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
attempt_no: Mapped[int] = mapped_column(
|
||||
INTEGER, nullable=False, server_default=text("1")
|
||||
)
|
||||
node_status: Mapped[str] = mapped_column(
|
||||
String(24),
|
||||
nullable=False,
|
||||
server_default=text("'queued'"),
|
||||
comment="queued/running/succeeded/failed/skipped/cancelled/timed_out",
|
||||
)
|
||||
state_version: Mapped[int] = mapped_column(
|
||||
INTEGER, nullable=False, server_default=text("0"), comment="乐观锁版本"
|
||||
)
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3), nullable=False, server_default=text("CURRENT_TIMESTAMP(3)")
|
||||
)
|
||||
started_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
finished_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
duration_ms: Mapped[Optional[int]] = mapped_column(BIGINT)
|
||||
exit_code: Mapped[Optional[int]] = mapped_column(Integer)
|
||||
message: Mapped[Optional[str]] = mapped_column(String(2000))
|
||||
metrics_json: Mapped[Optional[dict]] = mapped_column(JSON)
|
||||
logs_object_id: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
result_object_id: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
is_deleted: Mapped[int] = mapped_column(
|
||||
TINYINT(1), nullable=False, server_default=text("0")
|
||||
)
|
||||
deleted_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
@@ -0,0 +1,148 @@
|
||||
import datetime
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy import Index, String, text
|
||||
from sqlalchemy.dialects.mysql import BIGINT, CHAR, DATETIME, INTEGER, TINYINT
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from common.db.base import Base
|
||||
|
||||
|
||||
class Scripts(Base):
|
||||
__tablename__ = "scripts"
|
||||
__table_args__ = (
|
||||
Index("idx_scripts_owner", "owner_user_id", "status"),
|
||||
Index(
|
||||
"idx_scripts_workspace",
|
||||
"workspace_id",
|
||||
"script_type",
|
||||
"visibility",
|
||||
"status",
|
||||
),
|
||||
Index("uk_scripts_current_object", "current_object_id", unique=True),
|
||||
Index(
|
||||
"uk_scripts_workspace_name",
|
||||
"workspace_id",
|
||||
"script_name",
|
||||
"script_type",
|
||||
unique=True,
|
||||
),
|
||||
{"comment": "可执行 Python/Notebook 脚本"},
|
||||
)
|
||||
|
||||
script_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
workspace_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
current_object_id: Mapped[str] = mapped_column(
|
||||
CHAR(26), nullable=False, comment="当前工作副本"
|
||||
)
|
||||
owner_user_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
script_name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
script_type: Mapped[str] = mapped_column(
|
||||
String(16), nullable=False, comment="python/notebook"
|
||||
)
|
||||
visibility: Mapped[str] = mapped_column(
|
||||
String(16), nullable=False, server_default=text("'private'")
|
||||
)
|
||||
status: Mapped[str] = mapped_column(
|
||||
String(16), nullable=False, server_default=text("'active'")
|
||||
)
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3), nullable=False, server_default=text("CURRENT_TIMESTAMP(3)")
|
||||
)
|
||||
updated_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3),
|
||||
nullable=False,
|
||||
server_default=text("CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"),
|
||||
)
|
||||
is_deleted: Mapped[int] = mapped_column(
|
||||
TINYINT(1), nullable=False, server_default=text("0")
|
||||
)
|
||||
is_locked: Mapped[int] = mapped_column(
|
||||
TINYINT(1), nullable=False, server_default=text("1")
|
||||
)
|
||||
deleted_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
|
||||
|
||||
class NotebookSnapshots(Base):
|
||||
__tablename__ = "notebook_snapshots"
|
||||
__table_args__ = (
|
||||
Index("fk_snapshots_created_by", "created_by"),
|
||||
Index("fk_snapshots_source_object", "source_object_id"),
|
||||
Index("idx_snapshots_workspace_created", "workspace_id", "created_at"),
|
||||
Index("uk_snapshots_artifact", "artifact_object_id", unique=True),
|
||||
Index("uk_snapshots_script_hash", "script_id", "content_hash", unique=True),
|
||||
{"comment": "Notebook 开发快照,append-only"},
|
||||
)
|
||||
|
||||
snapshot_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
workspace_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
script_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
source_object_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
artifact_object_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
snapshot_name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
content_hash: Mapped[str] = mapped_column(CHAR(64), nullable=False)
|
||||
outputs_stripped: Mapped[int] = mapped_column(
|
||||
TINYINT(1), nullable=False, server_default=text("1")
|
||||
)
|
||||
created_by: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3), nullable=False, server_default=text("CURRENT_TIMESTAMP(3)")
|
||||
)
|
||||
description: Mapped[Optional[str]] = mapped_column(String(1000))
|
||||
is_deleted: Mapped[int] = mapped_column(
|
||||
TINYINT(1), nullable=False, server_default=text("0")
|
||||
)
|
||||
deleted_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
|
||||
|
||||
class Versions(Base):
|
||||
__tablename__ = "versions"
|
||||
__table_args__ = (
|
||||
Index("fk_versions_source_object", "source_object_id"),
|
||||
Index("idx_versions_creator", "created_by", "created_at"),
|
||||
Index("idx_versions_workspace_created", "workspace_id", "created_at"),
|
||||
Index("uk_versions_artifact", "artifact_object_id", unique=True),
|
||||
Index("uk_versions_script_hash", "script_id", "content_hash", unique=True),
|
||||
Index("uk_versions_script_no", "script_id", "version_no", unique=True),
|
||||
{"comment": "不可变稳定版本;调度节点必须引用 versions_id"},
|
||||
)
|
||||
|
||||
versions_id: Mapped[str] = mapped_column(
|
||||
CHAR(26), primary_key=True, comment="稳定版本唯一 ID"
|
||||
)
|
||||
workspace_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
script_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
source_object_id: Mapped[str] = mapped_column(
|
||||
CHAR(26), nullable=False, comment="发布时的源对象"
|
||||
)
|
||||
artifact_object_id: Mapped[str] = mapped_column(
|
||||
CHAR(26), nullable=False, comment="RustFS 不可变版本制品"
|
||||
)
|
||||
version_no: Mapped[int] = mapped_column(INTEGER, nullable=False)
|
||||
version_label: Mapped[str] = mapped_column(
|
||||
String(32), nullable=False, comment="例如 v1.0"
|
||||
)
|
||||
source_path: Mapped[str] = mapped_column(
|
||||
String(1024), nullable=False, comment="发布时路径快照"
|
||||
)
|
||||
artifact_path: Mapped[str] = mapped_column(String(1500), nullable=False)
|
||||
content_hash: Mapped[str] = mapped_column(CHAR(64), nullable=False)
|
||||
file_size_bytes: Mapped[int] = mapped_column(
|
||||
BIGINT, nullable=False, server_default=text("0")
|
||||
)
|
||||
visibility: Mapped[str] = mapped_column(
|
||||
String(16), nullable=False, server_default=text("'private'")
|
||||
)
|
||||
created_by: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3), nullable=False, server_default=text("CURRENT_TIMESTAMP(3)")
|
||||
)
|
||||
release_note: Mapped[Optional[str]] = mapped_column(String(1000))
|
||||
schedule_hidden_at: Mapped[Optional[datetime.datetime]] = mapped_column(
|
||||
DATETIME(fsp=3),
|
||||
comment="从调度稳定版本列表移除的时间;不影响版本和运行历史",
|
||||
)
|
||||
is_deleted: Mapped[int] = mapped_column(
|
||||
TINYINT(1), nullable=False, server_default=text("0")
|
||||
)
|
||||
deleted_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
@@ -0,0 +1,198 @@
|
||||
import datetime
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy import BINARY, Index, JSON, String, text
|
||||
from sqlalchemy.dialects.mysql import BIGINT, CHAR, DATETIME, TINYINT
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from common.db.base import Base
|
||||
|
||||
|
||||
class StorageObjects(Base):
|
||||
__tablename__ = "storage_objects"
|
||||
__table_args__ = (
|
||||
Index("fk_storage_created_by", "created_by"),
|
||||
Index("idx_storage_content_hash", "content_hash"),
|
||||
Index("idx_storage_owner", "owner_user_id", "object_status"),
|
||||
Index("idx_storage_parent", "parent_object_id"),
|
||||
Index(
|
||||
"idx_storage_workspace_usage",
|
||||
"workspace_id",
|
||||
"usage_type",
|
||||
"object_status",
|
||||
),
|
||||
Index(
|
||||
"uk_storage_bucket_key",
|
||||
"storage_backend",
|
||||
"bucket_name",
|
||||
"object_key_hash",
|
||||
unique=True,
|
||||
),
|
||||
Index(
|
||||
"uk_storage_workspace_path",
|
||||
"workspace_id",
|
||||
"storage_backend",
|
||||
"path_hash",
|
||||
unique=True,
|
||||
),
|
||||
{"comment": "Workspace 文件和 RustFS 对象的统一元数据"},
|
||||
)
|
||||
|
||||
storage_object_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
workspace_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
object_type: Mapped[str] = mapped_column(
|
||||
String(16), nullable=False, comment="file/directory"
|
||||
)
|
||||
usage_type: Mapped[str] = mapped_column(
|
||||
String(32),
|
||||
nullable=False,
|
||||
comment="working_copy/public_script/data_resource/version_artifact/snapshot/run_log/run_result",
|
||||
)
|
||||
storage_backend: Mapped[str] = mapped_column(
|
||||
String(16), nullable=False, comment="workspace_fs/rustfs"
|
||||
)
|
||||
storage_uri: Mapped[str] = mapped_column(String(1500), nullable=False)
|
||||
file_name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
size_bytes: Mapped[int] = mapped_column(
|
||||
BIGINT, nullable=False, server_default=text("0")
|
||||
)
|
||||
visibility: Mapped[str] = mapped_column(
|
||||
String(16),
|
||||
nullable=False,
|
||||
server_default=text("'private'"),
|
||||
comment="private/workspace/public",
|
||||
)
|
||||
is_immutable: Mapped[int] = mapped_column(
|
||||
TINYINT(1), nullable=False, server_default=text("0")
|
||||
)
|
||||
object_status: Mapped[str] = mapped_column(
|
||||
String(24),
|
||||
nullable=False,
|
||||
server_default=text("'available'"),
|
||||
comment="uploading/available/deleting/deleted/failed",
|
||||
)
|
||||
created_by: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3), nullable=False, server_default=text("CURRENT_TIMESTAMP(3)")
|
||||
)
|
||||
updated_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3),
|
||||
nullable=False,
|
||||
server_default=text("CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"),
|
||||
)
|
||||
owner_user_id: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
parent_object_id: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
relative_path: Mapped[Optional[str]] = mapped_column(
|
||||
String(1024), comment="Workspace 相对路径"
|
||||
)
|
||||
path_hash: Mapped[Optional[bytes]] = mapped_column(
|
||||
BINARY(32), comment="SHA-256(relative_path),由应用写入"
|
||||
)
|
||||
bucket_name: Mapped[Optional[str]] = mapped_column(String(128))
|
||||
object_key: Mapped[Optional[str]] = mapped_column(String(1024))
|
||||
object_key_hash: Mapped[Optional[bytes]] = mapped_column(
|
||||
BINARY(32), comment="SHA-256(object_key),由应用写入"
|
||||
)
|
||||
file_extension: Mapped[Optional[str]] = mapped_column(String(32))
|
||||
mime_type: Mapped[Optional[str]] = mapped_column(String(255))
|
||||
content_hash: Mapped[Optional[str]] = mapped_column(
|
||||
CHAR(64), comment="SHA-256 hex"
|
||||
)
|
||||
object_etag: Mapped[Optional[str]] = mapped_column(String(255))
|
||||
is_deleted: Mapped[int] = mapped_column(
|
||||
TINYINT(1), nullable=False, server_default=text("0")
|
||||
)
|
||||
deleted_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
|
||||
|
||||
class DataResources(Base):
|
||||
__tablename__ = "data_resources"
|
||||
__table_args__ = (
|
||||
Index("idx_data_resources_owner", "owner_user_id", "status"),
|
||||
Index(
|
||||
"idx_data_resources_workspace",
|
||||
"workspace_id",
|
||||
"visibility",
|
||||
"status",
|
||||
),
|
||||
Index("uk_data_resources_object", "storage_object_id", unique=True),
|
||||
{"comment": "数据资源"},
|
||||
)
|
||||
|
||||
resource_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
workspace_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
storage_object_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
owner_user_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
resource_name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
visibility: Mapped[str] = mapped_column(
|
||||
String(16), nullable=False, server_default=text("'private'")
|
||||
)
|
||||
status: Mapped[str] = mapped_column(
|
||||
String(16), nullable=False, server_default=text("'active'")
|
||||
)
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3), nullable=False, server_default=text("CURRENT_TIMESTAMP(3)")
|
||||
)
|
||||
updated_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3),
|
||||
nullable=False,
|
||||
server_default=text("CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"),
|
||||
)
|
||||
description: Mapped[Optional[str]] = mapped_column(String(1000))
|
||||
schema_json: Mapped[Optional[dict]] = mapped_column(
|
||||
JSON, comment="字段结构、行数等可选元数据"
|
||||
)
|
||||
is_deleted: Mapped[int] = mapped_column(
|
||||
TINYINT(1), nullable=False, server_default=text("0")
|
||||
)
|
||||
deleted_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
|
||||
|
||||
class UploadSessions(Base):
|
||||
__tablename__ = "upload_sessions"
|
||||
__table_args__ = (
|
||||
Index("fk_upload_sessions_storage_object", "storage_object_id"),
|
||||
Index("fk_upload_sessions_user", "user_id"),
|
||||
Index("idx_upload_sessions_expiry", "upload_status", "expires_at"),
|
||||
Index(
|
||||
"idx_upload_sessions_object_key", "bucket_name", "object_key_hash"
|
||||
),
|
||||
Index(
|
||||
"idx_upload_sessions_workspace", "workspace_id", "user_id", "created_at"
|
||||
),
|
||||
Index("uk_upload_sessions_idempotency", "idempotency_key", unique=True),
|
||||
{"comment": "RustFS 预签名上传会话;URL 本身不持久化"},
|
||||
)
|
||||
|
||||
upload_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
workspace_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
user_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
idempotency_key: Mapped[str] = mapped_column(String(128), nullable=False)
|
||||
bucket_name: Mapped[str] = mapped_column(String(128), nullable=False)
|
||||
object_key: Mapped[str] = mapped_column(String(1024), nullable=False)
|
||||
object_key_hash: Mapped[bytes] = mapped_column(BINARY(32), nullable=False)
|
||||
upload_status: Mapped[str] = mapped_column(
|
||||
String(24),
|
||||
nullable=False,
|
||||
server_default=text("'created'"),
|
||||
comment="created/uploading/completed/expired/aborted/failed",
|
||||
)
|
||||
expires_at: Mapped[datetime.datetime] = mapped_column(DATETIME(fsp=3), nullable=False)
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3), nullable=False, server_default=text("CURRENT_TIMESTAMP(3)")
|
||||
)
|
||||
updated_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3),
|
||||
nullable=False,
|
||||
server_default=text("CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"),
|
||||
)
|
||||
multipart_upload_id: Mapped[Optional[str]] = mapped_column(String(255))
|
||||
expected_size_bytes: Mapped[Optional[int]] = mapped_column(BIGINT)
|
||||
expected_hash: Mapped[Optional[str]] = mapped_column(CHAR(64))
|
||||
content_type: Mapped[Optional[str]] = mapped_column(String(255))
|
||||
storage_object_id: Mapped[Optional[str]] = mapped_column(CHAR(26))
|
||||
completed_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
is_deleted: Mapped[int] = mapped_column(
|
||||
TINYINT(1), nullable=False, server_default=text("0")
|
||||
)
|
||||
deleted_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
@@ -0,0 +1,85 @@
|
||||
import datetime
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy import Index, String, text
|
||||
from sqlalchemy.dialects.mysql import BIGINT, CHAR, DATETIME, TINYINT
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from common.db.base import Base
|
||||
|
||||
|
||||
class Workspaces(Base):
|
||||
__tablename__ = "workspaces"
|
||||
__table_args__ = (
|
||||
Index("fk_workspaces_created_by", "created_by"),
|
||||
Index("idx_workspaces_status", "status"),
|
||||
Index("uk_workspaces_code", "workspace_code", unique=True),
|
||||
{"comment": "Workspace"},
|
||||
)
|
||||
|
||||
workspace_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
workspace_code: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
workspace_name: Mapped[str] = mapped_column(String(150), nullable=False)
|
||||
active_root_uri: Mapped[str] = mapped_column(
|
||||
String(1500), nullable=False, comment="活动工作区,建议 NFS/PVC/file URI"
|
||||
)
|
||||
quota_bytes: Mapped[int] = mapped_column(
|
||||
BIGINT, nullable=False, server_default=text("0"), comment="0 表示不限额"
|
||||
)
|
||||
used_bytes: Mapped[int] = mapped_column(
|
||||
BIGINT, nullable=False, server_default=text("0")
|
||||
)
|
||||
status: Mapped[str] = mapped_column(
|
||||
String(24),
|
||||
nullable=False,
|
||||
server_default=text("'active'"),
|
||||
comment="creating/active/suspended/deleting/deleted",
|
||||
)
|
||||
created_by: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3), nullable=False, server_default=text("CURRENT_TIMESTAMP(3)")
|
||||
)
|
||||
updated_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3),
|
||||
nullable=False,
|
||||
server_default=text("CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"),
|
||||
)
|
||||
description: Mapped[Optional[str]] = mapped_column(String(1000))
|
||||
artifact_bucket: Mapped[Optional[str]] = mapped_column(
|
||||
String(128), comment="RustFS bucket"
|
||||
)
|
||||
artifact_prefix: Mapped[Optional[str]] = mapped_column(
|
||||
String(512), comment="RustFS object key prefix"
|
||||
)
|
||||
is_deleted: Mapped[int] = mapped_column(
|
||||
TINYINT(1), nullable=False, server_default=text("0")
|
||||
)
|
||||
deleted_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
|
||||
|
||||
class WorkspaceMembers(Base):
|
||||
__tablename__ = "workspace_members"
|
||||
__table_args__ = (
|
||||
Index("idx_workspace_members_role", "role_id"),
|
||||
Index("idx_workspace_members_user", "user_id", "member_status"),
|
||||
{"comment": "Workspace 成员与角色"},
|
||||
)
|
||||
|
||||
workspace_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
user_id: Mapped[str] = mapped_column(CHAR(26), primary_key=True)
|
||||
role_id: Mapped[str] = mapped_column(CHAR(26), nullable=False)
|
||||
member_status: Mapped[str] = mapped_column(
|
||||
String(16), nullable=False, server_default=text("'active'")
|
||||
)
|
||||
joined_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3), nullable=False, server_default=text("CURRENT_TIMESTAMP(3)")
|
||||
)
|
||||
updated_at: Mapped[datetime.datetime] = mapped_column(
|
||||
DATETIME(fsp=3),
|
||||
nullable=False,
|
||||
server_default=text("CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"),
|
||||
)
|
||||
is_deleted: Mapped[int] = mapped_column(
|
||||
TINYINT(1), nullable=False, server_default=text("0")
|
||||
)
|
||||
deleted_at: Mapped[Optional[datetime.datetime]] = mapped_column(DATETIME(fsp=3))
|
||||
@@ -4,7 +4,7 @@ from sqlalchemy import engine_from_config
|
||||
from sqlalchemy import pool
|
||||
|
||||
from alembic import context
|
||||
from common.db.base import Base
|
||||
from common.db.models import Base
|
||||
# this is the Alembic Config object, which provides
|
||||
# access to the values within the .ini file in use.
|
||||
config = context.config
|
||||
|
||||
@@ -1,7 +1,59 @@
|
||||
# coding=utf-8
|
||||
"""Shared low-level utilities used across services.
|
||||
|
||||
Currently home to two general-purpose helpers that have no runtime-
|
||||
specific concerns:
|
||||
|
||||
- :func:`get_free_port` asks the kernel for a currently-unused TCP port
|
||||
by binding to ``:0`` and reading back the assigned port number.
|
||||
- :func:`start_process` launches a subprocess with stdout/stderr merged
|
||||
into a per-pid log file under ``log_dir`` and returns the final log
|
||||
path so callers can log it themselves with whatever logger they use.
|
||||
"""
|
||||
@Time :2026/7/27
|
||||
@Author :tao.chen
|
||||
"""
|
||||
def hello_world():
|
||||
return 'Hello World!'
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import socket
|
||||
import subprocess
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def get_free_port() -> int:
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||
s.bind(("", 0))
|
||||
s.listen(1)
|
||||
port = s.getsockname()[1]
|
||||
return port
|
||||
|
||||
|
||||
def start_process(
|
||||
cmd: list[str],
|
||||
workspace_path: Path,
|
||||
log_dir: str | Path = "/tmp/process_logs",
|
||||
) -> tuple[subprocess.Popen, Path]:
|
||||
"""Launch ``cmd`` as a subprocess and return ``(process, log_file)``.
|
||||
|
||||
stderr is merged into a per-pid log file under ``log_dir``; the
|
||||
temp ``process_start_*.log`` is renamed to ``process_<pid>_*.log``
|
||||
once the real pid is known. The caller logs "I started this" with
|
||||
its own context — this function does not log on its own.
|
||||
"""
|
||||
log_dir_path = Path(log_dir)
|
||||
log_dir_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
start_time = time.strftime("%Y%m%d_%H%M%S")
|
||||
temp_log = log_dir_path / f"process_start_{start_time}.log"
|
||||
|
||||
with open(temp_log, "a", buffering=1) as log_file:
|
||||
process = subprocess.Popen(
|
||||
cmd,
|
||||
cwd=workspace_path,
|
||||
stdout=log_file,
|
||||
stderr=subprocess.STDOUT,
|
||||
start_new_session=True,
|
||||
)
|
||||
|
||||
final_log = log_dir_path / f"process_{process.pid}_{start_time}.log"
|
||||
temp_log.replace(final_log)
|
||||
|
||||
return process, final_log
|
||||
Reference in New Issue
Block a user