This commit is contained in:
tao.chen
2026-07-30 18:57:18 +08:00
parent ff7fc4ef1d
commit c1e15758a3
31 changed files with 2648 additions and 3356 deletions
+3 -6
View File
@@ -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
-878
View File
@@ -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')
+50 -5
View File
@@ -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",
]
+38
View File
@@ -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))
+79
View File
@@ -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))
+112
View File
@@ -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))
+117
View File
@@ -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))
+168
View File
@@ -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))
+245
View File
@@ -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))
+148
View File
@@ -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))
+198
View File
@@ -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))
+85
View File
@@ -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))
+1 -1
View File
@@ -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
+58 -6
View File
@@ -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