Files
hunjing/backend/app/models/jingsheng.py
T

85 lines
4.1 KiB
Python

"""镜生域模型:灵胚、执镜关系、命台、魂籍(data-model.md §1)。
- 灵胚只有预备身份,启镜(activate)才产生正式人物与执镜关系;
- 命台人物粒度唯一(UNIQUE(character_id)),租约到期可被接管,epoch 递增;
- 魂籍版本不可变(UNIQUE(character_id, version)),签名绑定人物与版本。
"""
from datetime import datetime
from sqlalchemy import DateTime, ForeignKey, Integer, String, Text, UniqueConstraint
from sqlalchemy.orm import Mapped, mapped_column
from app.core.ids import new_custodianship_id, new_embryo_id, new_identity_record_id, new_seat_id
from app.core.time import utc_now
from app.db.base import Base
class EmbryoDraft(Base):
"""灵胚:启镜前的可审阅草稿。放弃灵胚是删除草稿,不是人物死亡。"""
__tablename__ = "embryo_drafts"
id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_embryo_id)
owner_scope: Mapped[str] = mapped_column(String(80), nullable=False, index=True)
# drafting / previewing / confirming / activated / abandoned
status: Mapped[str] = mapped_column(String(40), nullable=False, default="drafting", index=True)
# original / adopted / imported / serendipity
source: Mapped[str] = mapped_column(String(40), nullable=False, default="original")
name: Mapped[str] = mapped_column(String(120), nullable=False)
description: Mapped[str | None] = mapped_column(Text)
identity_baseline_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
# 照心试演引用:[{run_id, prompt, performed_at}],内容在事件日志,不入正式经历
rehearsals_json: Mapped[str] = mapped_column(Text, nullable=False, default="[]")
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utc_now, onupdate=utc_now
)
class Custodianship(Base):
"""执镜关系:治理责任(1.0 一对一),与缘契(covenants)严格分离。"""
__tablename__ = "custodianships"
id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_custodianship_id)
character_id: Mapped[str] = mapped_column(
String(80), ForeignKey("characters.id"), nullable=False, unique=True, index=True
)
custodian_scope: Mapped[str] = mapped_column(String(80), nullable=False, index=True)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
class RuntimeSeat(Base):
"""命台:人物唯一权威运行位置;holder 持有租约,过期可接管,epoch 每次获取递增。"""
__tablename__ = "runtime_seats"
id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_seat_id)
character_id: Mapped[str] = mapped_column(
String(80), ForeignKey("characters.id"), nullable=False, unique=True, index=True
)
epoch: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
holder: Mapped[str | None] = mapped_column(String(120))
lease_expires_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utc_now, onupdate=utc_now
)
class IdentityRecord(Base):
"""魂籍:可验证、版本化的身份与状态包。版本不可变;复制不能凭相同 ID 冒充原人物。"""
__tablename__ = "identity_records"
__table_args__ = (UniqueConstraint("character_id", "version", name="uq_identity_char_version"),)
id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_identity_record_id)
character_id: Mapped[str] = mapped_column(
String(80), ForeignKey("characters.id"), nullable=False, index=True
)
version: Mapped[int] = mapped_column(Integer, nullable=False)
payload_json: Mapped[str] = mapped_column(Text, nullable=False)
digest: Mapped[str] = mapped_column(String(64), nullable=False)
signature: Mapped[str] = mapped_column(String(128), nullable=False)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)