363 lines
17 KiB
Python
363 lines
17 KiB
Python
# ============================================================
|
||
# 主数据表模型(moduleId: db-models, 可重生 ✅)
|
||
# 设计:projects 多项目隔离;master_records 宽表承载 world 主数据键
|
||
# (payload=完整记录 JSON,投影零损耗;code/name 建索引供查询);
|
||
# routing_templates 行业工艺路线模板库(平台级,跨项目共享)。
|
||
# ============================================================
|
||
from __future__ import annotations
|
||
|
||
from sqlalchemy import (
|
||
JSON,
|
||
BigInteger,
|
||
Boolean,
|
||
Float,
|
||
ForeignKey,
|
||
Index,
|
||
Integer,
|
||
String,
|
||
Text,
|
||
UniqueConstraint,
|
||
)
|
||
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
|
||
|
||
|
||
class Base(DeclarativeBase):
|
||
pass
|
||
|
||
|
||
class TenantAuditMixin:
|
||
tenant_uuid: Mapped[str] = mapped_column(String(32), nullable=False, index=True, default="platform")
|
||
creator_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True)
|
||
updater_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True)
|
||
updated_at: Mapped[str] = mapped_column(String(32), default="")
|
||
deleted: Mapped[int] = mapped_column(BigInteger, nullable=False, default=0)
|
||
|
||
|
||
class Project(TenantAuditMixin, Base):
|
||
"""项目/租户:康尼、演示厂……主数据按项目隔离。"""
|
||
__tablename__ = "projects"
|
||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||
code: Mapped[str] = mapped_column(String(64), index=True)
|
||
name: Mapped[str] = mapped_column(String(128), default="")
|
||
industry: Mapped[str] = mapped_column(String(64), default="machining")
|
||
active: Mapped[bool] = mapped_column(Boolean, default=False)
|
||
owner_user_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True)
|
||
data_version: Mapped[int] = mapped_column(Integer, default=0)
|
||
created_at: Mapped[str] = mapped_column(String(32), default="")
|
||
|
||
__table_args__ = (
|
||
UniqueConstraint("tenant_uuid", "code", "deleted", name="uk_projects_tenant_code"),
|
||
)
|
||
|
||
|
||
class MasterRecord(TenantAuditMixin, Base):
|
||
"""主数据宽表:一行 = world 某主数据表的一条记录(payload 全量 JSON)。
|
||
|
||
table_key 对齐 world 键名(factories/routingSteps/flexRoutings/salesOrders…),
|
||
投影时按 (project_id, table_key, seq) 还原顺序,保证与导入时零差异。
|
||
"""
|
||
__tablename__ = "master_records"
|
||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||
project_id: Mapped[int] = mapped_column(ForeignKey("projects.id"), index=True)
|
||
table_key: Mapped[str] = mapped_column(String(64), index=True)
|
||
seq: Mapped[int] = mapped_column(Integer, default=0)
|
||
rec_id: Mapped[str] = mapped_column(String(64), default="") # 原记录 id/code(辅助定位)
|
||
code: Mapped[str] = mapped_column(String(128), default="", index=True)
|
||
name: Mapped[str] = mapped_column(String(256), default="")
|
||
payload: Mapped[dict] = mapped_column(JSON)
|
||
|
||
__table_args__ = (
|
||
Index("ix_master_tenant_project_table", "tenant_uuid", "project_id", "table_key"),
|
||
)
|
||
|
||
|
||
class ProjectSetting(TenantAuditMixin, Base):
|
||
"""项目级字典配置:scheduleParams / constraintProfile / flexParams。"""
|
||
__tablename__ = "project_settings"
|
||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||
project_id: Mapped[int] = mapped_column(ForeignKey("projects.id"), index=True)
|
||
key: Mapped[str] = mapped_column(String(64), index=True)
|
||
payload: Mapped[dict] = mapped_column(JSON)
|
||
|
||
__table_args__ = (
|
||
Index("ix_settings_tenant_project_key", "tenant_uuid", "project_id", "key", unique=True),
|
||
)
|
||
|
||
|
||
class RoutingTemplate(TenantAuditMixin, Base):
|
||
"""行业工艺路线模板(平台级):机加工车/铣/钻/磨/装配等生成模式的结构化形态。"""
|
||
__tablename__ = "routing_templates"
|
||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||
code: Mapped[str] = mapped_column(String(64), index=True)
|
||
name: Mapped[str] = mapped_column(String(128))
|
||
industry: Mapped[str] = mapped_column(String(64), default="machining")
|
||
category: Mapped[str] = mapped_column(String(64), default="") # 车削/铣削/装配…
|
||
keywords: Mapped[str] = mapped_column(Text, default="") # 逗号分隔,检索匹配
|
||
description: Mapped[str] = mapped_column(Text, default="")
|
||
asset_id: Mapped[str] = mapped_column(String(32), default="") # 关联知识资产(RAG 出处)
|
||
source: Mapped[str] = mapped_column(String(128), default="builtin")
|
||
|
||
__table_args__ = (
|
||
UniqueConstraint("tenant_uuid", "code", "deleted", name="uk_routing_template_tenant_code"),
|
||
)
|
||
|
||
|
||
class RoutingTemplateStep(TenantAuditMixin, Base):
|
||
"""模板工步:含推荐工时区间与设备类型(供小工厂一键生成路线)。"""
|
||
__tablename__ = "routing_template_steps"
|
||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||
template_id: Mapped[int] = mapped_column(ForeignKey("routing_templates.id"), index=True)
|
||
seq: Mapped[int] = mapped_column(Integer)
|
||
operation_code: Mapped[str] = mapped_column(String(64))
|
||
operation_name: Mapped[str] = mapped_column(String(128))
|
||
equipment_type: Mapped[str] = mapped_column(String(128), default="")
|
||
std_min_low: Mapped[float] = mapped_column(Float, default=0) # 推荐单件工时下限(分钟)
|
||
std_min_high: Mapped[float] = mapped_column(Float, default=0) # 上限
|
||
setup_min: Mapped[float] = mapped_column(Float, default=10)
|
||
changeover_min: Mapped[float] = mapped_column(Float, default=10)
|
||
note: Mapped[str] = mapped_column(Text, default="")
|
||
|
||
|
||
class WorkspaceProject(TenantAuditMixin, Base):
|
||
__tablename__ = "aps_workspace_projects"
|
||
id: Mapped[str] = mapped_column(String(64), primary_key=True)
|
||
name: Mapped[str] = mapped_column(String(128), default="")
|
||
scope_label: Mapped[str] = mapped_column(String(128), default="未设定作用域")
|
||
owner_user_id: Mapped[int] = mapped_column(BigInteger, nullable=False, index=True)
|
||
work_dir: Mapped[str] = mapped_column(String(512), default="")
|
||
archived: Mapped[bool] = mapped_column(Boolean, default=False)
|
||
data_version: Mapped[int] = mapped_column(Integer, default=0)
|
||
created_at: Mapped[str] = mapped_column(String(32), default="")
|
||
|
||
__table_args__ = (
|
||
Index("ix_workspace_projects_tenant_owner", "tenant_uuid", "owner_user_id", "deleted"),
|
||
)
|
||
|
||
|
||
class ProjectMember(TenantAuditMixin, Base):
|
||
__tablename__ = "aps_project_members"
|
||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||
project_id: Mapped[str] = mapped_column(ForeignKey("aps_workspace_projects.id"), index=True)
|
||
user_id: Mapped[int] = mapped_column(BigInteger, nullable=False, index=True)
|
||
role: Mapped[str] = mapped_column(String(16), nullable=False, default="viewer")
|
||
status: Mapped[str] = mapped_column(String(16), nullable=False, default="active")
|
||
invited_by: Mapped[int | None] = mapped_column(BigInteger, nullable=True)
|
||
created_at: Mapped[str] = mapped_column(String(32), default="")
|
||
|
||
__table_args__ = (
|
||
UniqueConstraint("tenant_uuid", "project_id", "user_id", "deleted", name="uk_project_member"),
|
||
Index("ix_project_member_access", "tenant_uuid", "user_id", "status", "deleted"),
|
||
)
|
||
|
||
|
||
class UserWorkspace(TenantAuditMixin, Base):
|
||
__tablename__ = "aps_user_workspaces"
|
||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||
user_id: Mapped[int] = mapped_column(BigInteger, nullable=False)
|
||
active_project_id: Mapped[str] = mapped_column(String(64), default="__personal__")
|
||
active_session_id: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||
settings: Mapped[dict] = mapped_column(JSON, default=dict)
|
||
version: Mapped[int] = mapped_column(Integer, default=0)
|
||
created_at: Mapped[str] = mapped_column(String(32), default="")
|
||
|
||
__table_args__ = (
|
||
UniqueConstraint("tenant_uuid", "user_id", "deleted", name="uk_user_workspace"),
|
||
)
|
||
|
||
|
||
class ChatSessionRecord(TenantAuditMixin, Base):
|
||
__tablename__ = "aps_chat_sessions"
|
||
id: Mapped[str] = mapped_column(String(64), primary_key=True)
|
||
project_id: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||
owner_user_id: Mapped[int] = mapped_column(BigInteger, nullable=False, index=True)
|
||
scope: Mapped[str] = mapped_column(String(16), nullable=False, default="personal")
|
||
title: Mapped[str] = mapped_column(String(200), default="新话题")
|
||
status: Mapped[str] = mapped_column(String(16), default="running")
|
||
created_at: Mapped[str] = mapped_column(String(32), default="")
|
||
|
||
__table_args__ = (
|
||
Index("ix_chat_session_personal", "tenant_uuid", "owner_user_id", "scope", "deleted"),
|
||
Index("ix_chat_session_project", "tenant_uuid", "project_id", "scope", "deleted"),
|
||
)
|
||
|
||
|
||
class ChatMessageRecord(TenantAuditMixin, Base):
|
||
__tablename__ = "aps_chat_messages"
|
||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||
session_id: Mapped[str] = mapped_column(ForeignKey("aps_chat_sessions.id"), index=True)
|
||
sender_user_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True)
|
||
seq: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
||
payload: Mapped[dict] = mapped_column(JSON)
|
||
created_at: Mapped[str] = mapped_column(String(32), default="")
|
||
|
||
__table_args__ = (
|
||
Index("ix_chat_message_tenant_session_seq", "tenant_uuid", "session_id", "seq", unique=True),
|
||
)
|
||
|
||
|
||
class ProjectFileRecord(TenantAuditMixin, Base):
|
||
__tablename__ = "aps_project_files"
|
||
id: Mapped[str] = mapped_column(String(64), primary_key=True)
|
||
project_id: Mapped[str] = mapped_column(ForeignKey("aps_workspace_projects.id"), index=True)
|
||
name: Mapped[str] = mapped_column(String(256), default="")
|
||
kind: Mapped[str] = mapped_column(String(32), default="other")
|
||
note: Mapped[str] = mapped_column(Text, default="")
|
||
storage_key: Mapped[str] = mapped_column(String(512), default="")
|
||
created_at: Mapped[str] = mapped_column(String(32), default="")
|
||
|
||
|
||
class ScheduleRun(TenantAuditMixin, Base):
|
||
__tablename__ = "aps_schedule_runs"
|
||
id: Mapped[str] = mapped_column(String(64), primary_key=True)
|
||
project_id: Mapped[str] = mapped_column(ForeignKey("aps_workspace_projects.id"), index=True)
|
||
triggered_by: Mapped[int] = mapped_column(BigInteger, nullable=False)
|
||
input_version: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
||
status: Mapped[str] = mapped_column(String(24), nullable=False, default="pending")
|
||
started_at: Mapped[str] = mapped_column(String(32), default="")
|
||
finished_at: Mapped[str] = mapped_column(String(32), default="")
|
||
error: Mapped[str] = mapped_column(Text, default="")
|
||
created_at: Mapped[str] = mapped_column(String(32), default="")
|
||
|
||
__table_args__ = (
|
||
Index("ix_schedule_run_project_status", "tenant_uuid", "project_id", "status"),
|
||
)
|
||
|
||
|
||
class ScheduleResult(TenantAuditMixin, Base):
|
||
__tablename__ = "aps_schedule_results"
|
||
id: Mapped[str] = mapped_column(String(64), primary_key=True)
|
||
project_id: Mapped[str] = mapped_column(ForeignKey("aps_workspace_projects.id"), index=True)
|
||
run_id: Mapped[str] = mapped_column(ForeignKey("aps_schedule_runs.id"), index=True)
|
||
input_version: Mapped[int] = mapped_column(Integer, nullable=False)
|
||
result_version: Mapped[str] = mapped_column(String(64), nullable=False)
|
||
payload: Mapped[dict] = mapped_column(JSON)
|
||
created_at: Mapped[str] = mapped_column(String(32), default="")
|
||
|
||
|
||
class AuditEventRecord(TenantAuditMixin, Base):
|
||
__tablename__ = "aps_audit_events"
|
||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||
actor_user_id: Mapped[int] = mapped_column(BigInteger, nullable=False)
|
||
project_id: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||
action: Mapped[str] = mapped_column(String(128), nullable=False)
|
||
resource_type: Mapped[str] = mapped_column(String(64), nullable=False)
|
||
resource_id: Mapped[str] = mapped_column(String(128), default="")
|
||
detail: Mapped[dict] = mapped_column(JSON, default=dict)
|
||
created_at: Mapped[str] = mapped_column(String(32), default="")
|
||
|
||
__table_args__ = (
|
||
Index("ix_audit_tenant_actor_created", "tenant_uuid", "actor_user_id", "created_at"),
|
||
)
|
||
|
||
|
||
class ApprovalRequestRecord(Base):
|
||
"""Mutable approval state; transitions are guarded by ``revision`` CAS."""
|
||
|
||
__tablename__ = "aps_approval_requests"
|
||
confirm_id: Mapped[str] = mapped_column(String(64), primary_key=True)
|
||
tenant_uuid: Mapped[str] = mapped_column(String(32), nullable=False, index=True)
|
||
project_id: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
|
||
owner_user_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True)
|
||
action: Mapped[str] = mapped_column(String(128), nullable=False)
|
||
power: Mapped[str] = mapped_column(String(8), nullable=False)
|
||
params_hash: Mapped[str] = mapped_column(String(64), nullable=False)
|
||
status: Mapped[str] = mapped_column(String(24), nullable=False, default="PENDING")
|
||
approval_step: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
||
required_approvals: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
|
||
created_at_epoch: Mapped[float] = mapped_column(Float, nullable=False)
|
||
expires_at_epoch: Mapped[float] = mapped_column(Float, nullable=False)
|
||
revision: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
||
payload: Mapped[dict] = mapped_column(JSON, nullable=False)
|
||
|
||
__table_args__ = (
|
||
Index(
|
||
"ix_approval_request_scope_status_expiry",
|
||
"tenant_uuid",
|
||
"project_id",
|
||
"status",
|
||
"expires_at_epoch",
|
||
),
|
||
)
|
||
|
||
|
||
class ApprovalGrantRecord(Base):
|
||
"""One-time P3 execution grant. Only the token digest is persisted."""
|
||
|
||
__tablename__ = "aps_approval_grants"
|
||
token_hash: Mapped[str] = mapped_column(String(64), primary_key=True)
|
||
confirm_id: Mapped[str] = mapped_column(
|
||
ForeignKey("aps_approval_requests.confirm_id"), nullable=False, unique=True, index=True
|
||
)
|
||
tenant_uuid: Mapped[str] = mapped_column(String(32), nullable=False, index=True)
|
||
project_id: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
|
||
owner_user_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True)
|
||
action: Mapped[str] = mapped_column(String(128), nullable=False)
|
||
params_hash: Mapped[str] = mapped_column(String(64), nullable=False)
|
||
status: Mapped[str] = mapped_column(String(24), nullable=False, default="ACTIVE")
|
||
created_at_epoch: Mapped[float] = mapped_column(Float, nullable=False)
|
||
expires_at_epoch: Mapped[float] = mapped_column(Float, nullable=False)
|
||
consumed_at_epoch: Mapped[float | None] = mapped_column(Float, nullable=True)
|
||
revision: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
||
payload: Mapped[dict] = mapped_column(JSON, nullable=False)
|
||
|
||
__table_args__ = (
|
||
Index(
|
||
"ix_approval_grant_scope_status_expiry",
|
||
"tenant_uuid",
|
||
"project_id",
|
||
"status",
|
||
"expires_at_epoch",
|
||
),
|
||
)
|
||
|
||
|
||
class ApprovalEventRecord(Base):
|
||
"""Append-only approval history projection."""
|
||
|
||
__tablename__ = "aps_approval_events"
|
||
id: Mapped[int] = mapped_column(
|
||
BigInteger().with_variant(Integer, "sqlite"), primary_key=True, autoincrement=True
|
||
)
|
||
confirm_id: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||
tenant_uuid: Mapped[str] = mapped_column(String(32), nullable=False, index=True)
|
||
project_id: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
|
||
owner_user_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True)
|
||
action: Mapped[str] = mapped_column(String(128), nullable=False)
|
||
power: Mapped[str] = mapped_column(String(8), nullable=False)
|
||
status: Mapped[str] = mapped_column(String(32), nullable=False)
|
||
decided_at_epoch: Mapped[float] = mapped_column(Float, nullable=False)
|
||
payload: Mapped[dict] = mapped_column(JSON, nullable=False)
|
||
|
||
__table_args__ = (
|
||
Index(
|
||
"ix_approval_event_scope_order",
|
||
"tenant_uuid",
|
||
"project_id",
|
||
"id",
|
||
),
|
||
)
|
||
|
||
|
||
class LicenseActivation(TenantAuditMixin, Base):
|
||
"""A server-validated desktop entitlement bound to one installation."""
|
||
|
||
__tablename__ = "aps_license_activations"
|
||
id: Mapped[str] = mapped_column(String(64), primary_key=True)
|
||
code_hash: Mapped[str] = mapped_column(String(64), nullable=False)
|
||
code_hint: Mapped[str] = mapped_column(String(32), default="")
|
||
device_hash: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
|
||
user_id: Mapped[int] = mapped_column(BigInteger, nullable=False, index=True)
|
||
license_type: Mapped[str] = mapped_column(String(16), nullable=False)
|
||
status: Mapped[str] = mapped_column(String(16), nullable=False, default="active")
|
||
activated_at: Mapped[int] = mapped_column(BigInteger, nullable=False)
|
||
expires_at: Mapped[int | None] = mapped_column(BigInteger, nullable=True)
|
||
last_seen_at: Mapped[int] = mapped_column(BigInteger, nullable=False)
|
||
revoked_at: Mapped[int | None] = mapped_column(BigInteger, nullable=True)
|
||
created_at: Mapped[str] = mapped_column(String(32), default="")
|
||
|
||
__table_args__ = (
|
||
UniqueConstraint("code_hash", "deleted", name="uk_license_activation_code"),
|
||
Index("ix_license_device_status", "device_hash", "status", "deleted"),
|
||
Index("ix_license_tenant_user", "tenant_uuid", "user_id", "deleted"),
|
||
)
|