84 lines
2.5 KiB
Python
84 lines
2.5 KiB
Python
"""双时态事实仓储:写入与"按历史时点回放"查询。
|
||
|
||
对应需求 R3 / ADR-0002:
|
||
- 业务有效期 valid_from/valid_to(应用时间)
|
||
- 系统记录期 system_from/system_to(事务时间)
|
||
回放 = 给定 (as_of_valid, as_of_system) 在两条时间线上各取"包含该时点"的记录。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import datetime as dt
|
||
import uuid
|
||
|
||
from sqlalchemy import or_
|
||
from sqlalchemy.orm import Session
|
||
|
||
from app.datahub.models import BitemporalFact
|
||
|
||
|
||
def record_fact(
|
||
session: Session,
|
||
entity_id: uuid.UUID,
|
||
attr_name: str,
|
||
attr_value: dict,
|
||
valid_from: dt.datetime,
|
||
valid_to: dt.datetime | None = None,
|
||
data_version_id: uuid.UUID | None = None,
|
||
) -> BitemporalFact:
|
||
"""记录一条双时态事实(system_from 自动取当前事务时间)。"""
|
||
fact = BitemporalFact(
|
||
entity_id=entity_id,
|
||
attr_name=attr_name,
|
||
attr_value=attr_value,
|
||
valid_from=valid_from,
|
||
valid_to=valid_to,
|
||
data_version_id=data_version_id,
|
||
)
|
||
session.add(fact)
|
||
session.flush()
|
||
return fact
|
||
|
||
|
||
def as_of(
|
||
session: Session,
|
||
entity_id: uuid.UUID,
|
||
attr_name: str,
|
||
as_of_valid: dt.datetime,
|
||
as_of_system: dt.datetime | None = None,
|
||
) -> BitemporalFact | None:
|
||
"""回放:返回在给定业务时点且按给定系统时点可见的事实。
|
||
|
||
- 业务时间线:valid_from <= as_of_valid < valid_to(或为空表示至今)
|
||
- 系统时间线:system_from <= as_of_system < system_to(或为空表示当前可见)
|
||
"""
|
||
as_of_system = as_of_system or dt.datetime.now(dt.UTC)
|
||
|
||
q = (
|
||
session.query(BitemporalFact)
|
||
.filter(BitemporalFact.entity_id == entity_id)
|
||
.filter(BitemporalFact.attr_name == attr_name)
|
||
.filter(BitemporalFact.valid_from <= as_of_valid)
|
||
.filter(
|
||
or_(BitemporalFact.valid_to.is_(None), BitemporalFact.valid_to > as_of_valid)
|
||
)
|
||
.filter(BitemporalFact.system_from <= as_of_system)
|
||
.filter(
|
||
or_(
|
||
BitemporalFact.system_to.is_(None),
|
||
BitemporalFact.system_to > as_of_system,
|
||
)
|
||
)
|
||
.order_by(BitemporalFact.system_from.desc())
|
||
)
|
||
return q.first()
|
||
|
||
|
||
def close_fact(
|
||
session: Session, fact: BitemporalFact, system_to: dt.datetime | None = None
|
||
) -> None:
|
||
"""逻辑关闭一条事实的系统可见期(用于更正/失效,而非物理删除)。"""
|
||
fact.system_to = system_to or dt.datetime.now(dt.UTC)
|
||
session.add(fact)
|
||
session.flush()
|