项目十:事件溯源仓储管理系统(WMS)仿真实现

项目十:事件溯源仓储管理系统(WMS)仿真实现

基于Event Sourcing与CQRS的库存精准管理系统完整仿真


python 复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
================================================================================
项目十:事件溯源仓储管理系统(WMS)仿真
基于Event Sourcing与CQRS的库存精准管理系统
================================================================================
作者:AI Assistant
日期:2026-05-27
说明:本程序为教学仿真,使用内存存储模拟完整的事件溯源架构
================================================================================
"""

import json
import hashlib
import time
import random
import threading
import uuid
from datetime import datetime, timedelta
from dataclasses import dataclass, field, asdict
from typing import Dict, List, Optional, Callable, Any, Set
from enum import Enum, auto
from collections import defaultdict
import copy

# ==============================================================================
# 10.2.3 全局排序:Snowflake ID 实现
# ==============================================================================
class SnowflakeGenerator:
    """Snowflake ID生成器:保证全局唯一且有序"""

    def __init__(self, datacenter_id: int = 0, worker_id: int = 0):
        self.datacenter_id = datacenter_id
        self.worker_id = worker_id
        self.sequence = 0
        self.last_timestamp = -1
        self.twepoch = 1609459200000
        self.datacenter_id_bits = 5
        self.worker_id_bits = 5
        self.sequence_bits = 12
        self.max_datacenter_id = -1 ^ (-1 << self.datacenter_id_bits)
        self.max_worker_id = -1 ^ (-1 << self.worker_id_bits)
        self.sequence_mask = -1 ^ (-1 << self.sequence_bits)
        self.worker_id_shift = self.sequence_bits
        self.datacenter_id_shift = self.sequence_bits + self.worker_id_bits
        self.timestamp_left_shift = self.sequence_bits + self.worker_id_bits + self.datacenter_id_bits
        self.lock = threading.Lock()

    def _current_timestamp(self) -> int:
        return int(time.time() * 1000)

    def _til_next_millis(self, last_timestamp: int) -> int:
        timestamp = self._current_timestamp()
        while timestamp <= last_timestamp:
            timestamp = self._current_timestamp()
        return timestamp

    def next_id(self) -> int:
        with self.lock:
            timestamp = self._current_timestamp()
            if timestamp < self.last_timestamp:
                raise Exception("时钟回拨异常")
            if timestamp == self.last_timestamp:
                self.sequence = (self.sequence + 1) & self.sequence_mask
                if self.sequence == 0:
                    timestamp = self._til_next_millis(self.last_timestamp)
            else:
                self.sequence = 0
            self.last_timestamp = timestamp
            return ((timestamp - self.twepoch) << self.timestamp_left_shift) | \
                   (self.datacenter_id << self.datacenter_id_shift) | \
                   (self.worker_id << self.worker_id_shift) | self.sequence

snowflake = SnowflakeGenerator()

# ==============================================================================
# 10.1.1 库存事件设计:领域事件定义
# ==============================================================================
class EventType(Enum):
    """事件类型枚举"""
    STOCK_RECEIVED = "StockReceived"
    STOCK_MOVED = "StockMoved"
    STOCK_ALLOCATED = "StockAllocated"
    STOCK_SHIPPED = "StockShipped"
    STOCK_ADJUSTED = "StockAdjusted"

@dataclass
class DomainEvent:
    """领域事件基类"""
    event_id: int
    event_type: EventType
    aggregate_id: str
    aggregate_version: int
    timestamp: datetime
    payload: Dict[str, Any]
    previous_hash: str = ""
    event_hash: str = ""

    def compute_hash(self) -> str:
        """计算事件哈希:实现10.5.1不可篡改日志"""
        data = {
            "event_id": self.event_id,
            "event_type": self.event_type.value,
            "aggregate_id": self.aggregate_id,
            "aggregate_version": self.aggregate_version,
            "timestamp": self.timestamp.isoformat(),
            "payload": self.payload,
            "previous_hash": self.previous_hash
        }
        return hashlib.sha256(json.dumps(data, sort_keys=True, ensure_ascii=False).encode()).hexdigest()

    def finalize(self):
        """最终化事件:计算哈希"""
        self.event_hash = self.compute_hash()

# ==============================================================================
# 10.2.2 事件序列化:Protobuf模拟(使用JSON+Schema版本管理)
# ==============================================================================
class EventSerializer:
    """事件序列化器:模拟Protobuf二进制格式与Schema版本管理"""

    SCHEMA_VERSION = "1.0.0"

    @staticmethod
    def serialize(event: DomainEvent) -> bytes:
        """序列化事件为二进制(模拟Protobuf)"""
        data = {
            "schema_version": EventSerializer.SCHEMA_VERSION,
            "event_id": event.event_id,
            "event_type": event.event_type.value,
            "aggregate_id": event.aggregate_id,
            "aggregate_version": event.aggregate_version,
            "timestamp": event.timestamp.isoformat(),
            "payload": event.payload,
            "previous_hash": event.previous_hash,
            "event_hash": event.event_hash
        }
        return json.dumps(data, ensure_ascii=False).encode("utf-8")

    @staticmethod
    def deserialize(data: bytes) -> DomainEvent:
        """反序列化事件"""
        obj = json.loads(data.decode("utf-8"))
        event = DomainEvent(
            event_id=obj["event_id"],
            event_type=EventType(obj["event_type"]),
            aggregate_id=obj["aggregate_id"],
            aggregate_version=obj["aggregate_version"],
            timestamp=datetime.fromisoformat(obj["timestamp"]),
            payload=obj["payload"],
            previous_hash=obj["previous_hash"],
            event_hash=obj["event_hash"]
        )
        return event

# ==============================================================================
# 10.2.1 事件存储:内存事件存储引擎(模拟PostgreSQL JSONB)
# 10.2.4 归档策略:历史事件冷存储与压缩
# ==============================================================================
class EventStore:
    """事件存储引擎:追加写模型与版本号并发控制"""

    def __init__(self):
        self._streams: Dict[str, List[DomainEvent]] = defaultdict(list)
        self._global_events: Dict[int, DomainEvent] = {}
        self._processed_ids: Set[int] = set()
        self._archived: Dict[str, List[DomainEvent]] = defaultdict(list)
        self._archive_threshold = 50
        self._lock = threading.RLock()
        self._last_hashes: Dict[str, str] = {}

    def append(self, event: DomainEvent) -> bool:
        """追加事件:实现乐观并发控制"""
        with self._lock:
            # 10.4.2 幂等性保障
            if event.event_id in self._processed_ids:
                print(f"  [幂等性拦截] 事件 {event.event_id} 已存在,跳过处理")
                return False

            stream = self._streams[event.aggregate_id]

            # 10.4.1 乐观并发控制
            expected_version = len(stream)
            if event.aggregate_version != expected_version + 1:
                raise ConcurrentModificationException(
                    f"并发冲突:聚合根 {event.aggregate_id} 期望版本 {expected_version + 1},"
                    f"实际版本 {event.aggregate_version}"
                )

            # 10.5.1 不可篡改日志:区块链式事件链
            event.previous_hash = self._last_hashes.get(event.aggregate_id, "0" * 64)
            event.finalize()
            self._last_hashes[event.aggregate_id] = event.event_hash

            stream.append(event)
            self._global_events[event.event_id] = event
            self._processed_ids.add(event.event_id)

            # 10.2.4 归档策略检查
            self._check_archive(event.aggregate_id)
            return True

    def _check_archive(self, aggregate_id: str):
        """检查并执行归档"""
        stream = self._streams[aggregate_id]
        if len(stream) > self._archive_threshold:
            archive_count = len(stream) // 2
            archived_events = stream[:archive_count]
            self._archived[aggregate_id].extend(archived_events)
            self._streams[aggregate_id] = stream[archive_count:]
            print(f"  [归档] 聚合根 {aggregate_id} 的 {archive_count} 个事件已归档(冷存储)")

    def get_events(self, aggregate_id: str, from_version: int = 0) -> List[DomainEvent]:
        """获取聚合根事件流"""
        with self._lock:
            archived = self._archived.get(aggregate_id, [])
            active = self._streams.get(aggregate_id, [])
            all_events = archived + active
            return all_events[from_version:]

    def get_all_events(self) -> List[DomainEvent]:
        """获取所有事件(按全局顺序)"""
        with self._lock:
            return sorted(self._global_events.values(), key=lambda e: e.event_id)

    def get_event_by_id(self, event_id: int) -> Optional[DomainEvent]:
        return self._global_events.get(event_id)

    def verify_chain(self, aggregate_id: str) -> bool:
        """验证事件链完整性"""
        events = self.get_events(aggregate_id)
        for i, event in enumerate(events):
            if i == 0:
                continue
            if event.previous_hash != events[i-1].event_hash:
                return False
            if event.compute_hash() != event.event_hash:
                return False
        return True

class ConcurrentModificationException(Exception):
    """并发修改异常"""
    pass

# ==============================================================================
# 10.1.2 聚合根实现:ProductAggregate与LocationAggregate
# 10.1.3 快照机制
# ==============================================================================
class ProductAggregate:
    """商品聚合根:管理单个SKU的库存状态"""

    def __init__(self, sku: str):
        self.sku = sku
        self.version = 0
        self.total_quantity = 0
        self.available_quantity = 0
        self.allocated_quantity = 0
        self.locations: Dict[str, int] = {}
        self.pending_events: List[DomainEvent] = []
        self._snapshot_version = 0

    def apply(self, event: DomainEvent):
        """应用事件到聚合根状态"""
        if event.event_type == EventType.STOCK_RECEIVED:
            self._on_stock_received(event)
        elif event.event_type == EventType.STOCK_MOVED:
            self._on_stock_moved(event)
        elif event.event_type == EventType.STOCK_ALLOCATED:
            self._on_stock_allocated(event)
        elif event.event_type == EventType.STOCK_SHIPPED:
            self._on_stock_shipped(event)
        elif event.event_type == EventType.STOCK_ADJUSTED:
            self._on_stock_adjusted(event)
        self.version = event.aggregate_version

    def _on_stock_received(self, event: DomainEvent):
        qty = event.payload["quantity"]
        location = event.payload["location"]
        self.total_quantity += qty
        self.available_quantity += qty
        self.locations[location] = self.locations.get(location, 0) + qty

    def _on_stock_moved(self, event: DomainEvent):
        qty = event.payload["quantity"]
        from_loc = event.payload["from_location"]
        to_loc = event.payload["to_location"]
        self.locations[from_loc] -= qty
        self.locations[to_loc] = self.locations.get(to_loc, 0) + qty
        if self.locations[from_loc] == 0:
            del self.locations[from_loc]

    def _on_stock_allocated(self, event: DomainEvent):
        qty = event.payload["quantity"]
        self.available_quantity -= qty
        self.allocated_quantity += qty

    def _on_stock_shipped(self, event: DomainEvent):
        qty = event.payload["quantity"]
        location = event.payload["location"]
        self.total_quantity -= qty
        self.allocated_quantity -= qty
        self.locations[location] -= qty
        if self.locations[location] == 0:
            del self.locations[location]

    def _on_stock_adjusted(self, event: DomainEvent):
        diff = event.payload["difference"]
        location = event.payload["location"]
        self.total_quantity += diff
        self.available_quantity += diff
        self.locations[location] = self.locations.get(location, 0) + diff

    def receive_stock(self, quantity: int, location: str, batch_no: str) -> DomainEvent:
        """入库命令"""
        self.version += 1
        event = DomainEvent(
            event_id=snowflake.next_id(),
            event_type=EventType.STOCK_RECEIVED,
            aggregate_id=self.sku,
            aggregate_version=self.version,
            timestamp=datetime.now(),
            payload={"quantity": quantity, "location": location, "batch_no": batch_no, "operator": "操作员A"}
        )
        self.apply(event)
        self.pending_events.append(event)
        return event

    def move_stock(self, quantity: int, from_location: str, to_location: str) -> DomainEvent:
        """移库命令"""
        if self.locations.get(from_location, 0) < quantity:
            raise ValueError(f"库位 {from_location} 库存不足")
        self.version += 1
        event = DomainEvent(
            event_id=snowflake.next_id(),
            event_type=EventType.STOCK_MOVED,
            aggregate_id=self.sku,
            aggregate_version=self.version,
            timestamp=datetime.now(),
            payload={"quantity": quantity, "from_location": from_location, "to_location": to_location, "operator": "叉车工B"}
        )
        self.apply(event)
        self.pending_events.append(event)
        return event

    def allocate_stock(self, quantity: int, order_id: str) -> DomainEvent:
        """分配命令"""
        if self.available_quantity < quantity:
            raise ValueError(f"可用库存不足:需要 {quantity},可用 {self.available_quantity}")
        self.version += 1
        event = DomainEvent(
            event_id=snowflake.next_id(),
            event_type=EventType.STOCK_ALLOCATED,
            aggregate_id=self.sku,
            aggregate_version=self.version,
            timestamp=datetime.now(),
            payload={"quantity": quantity, "order_id": order_id, "operator": "系统"}
        )
        self.apply(event)
        self.pending_events.append(event)
        return event

    def ship_stock(self, quantity: int, location: str, order_id: str) -> DomainEvent:
        """出库命令"""
        self.version += 1
        event = DomainEvent(
            event_id=snowflake.next_id(),
            event_type=EventType.STOCK_SHIPPED,
            aggregate_id=self.sku,
            aggregate_version=self.version,
            timestamp=datetime.now(),
            payload={"quantity": quantity, "location": location, "order_id": order_id, "operator": "发货员C"}
        )
        self.apply(event)
        self.pending_events.append(event)
        return event

    def adjust_stock(self, location: str, physical_qty: int) -> DomainEvent:
        """盘点调整命令"""
        system_qty = self.locations.get(location, 0)
        diff = physical_qty - system_qty
        self.version += 1
        event = DomainEvent(
            event_id=snowflake.next_id(),
            event_type=EventType.STOCK_ADJUSTED,
            aggregate_id=self.sku,
            aggregate_version=self.version,
            timestamp=datetime.now(),
            payload={"location": location, "system_quantity": system_qty, "physical_quantity": physical_qty, "difference": diff, "operator": "盘点员D"}
        )
        self.apply(event)
        self.pending_events.append(event)
        return event

    def create_snapshot(self) -> Dict:
        """创建快照"""
        return {
            "sku": self.sku,
            "version": self.version,
            "total_quantity": self.total_quantity,
            "available_quantity": self.available_quantity,
            "allocated_quantity": self.allocated_quantity,
            "locations": copy.deepcopy(self.locations),
            "timestamp": datetime.now().isoformat()
        }

    @classmethod
    def restore_from_snapshot(cls, snapshot: Dict, events: List[DomainEvent]) -> "ProductAggregate":
        """从快照恢复聚合根"""
        aggregate = cls(snapshot["sku"])
        aggregate.version = snapshot["version"]
        aggregate.total_quantity = snapshot["total_quantity"]
        aggregate.available_quantity = snapshot["available_quantity"]
        aggregate.allocated_quantity = snapshot["allocated_quantity"]
        aggregate.locations = copy.deepcopy(snapshot["locations"])
        aggregate._snapshot_version = snapshot["version"]
        for event in events:
            aggregate.apply(event)
        return aggregate

class LocationAggregate:
    """库位聚合根"""

    def __init__(self, location_code: str):
        self.location_code = location_code
        self.version = 0
        self.items: Dict[str, int] = {}
        self.capacity = 1000
        self.pending_events: List[DomainEvent] = []

    def apply(self, event: DomainEvent):
        if event.event_type == EventType.STOCK_RECEIVED:
            sku = event.aggregate_id
            qty = event.payload["quantity"]
            self.items[sku] = self.items.get(sku, 0) + qty
        elif event.event_type == EventType.STOCK_MOVED:
            sku = event.aggregate_id
            qty = event.payload["quantity"]
            if event.payload["from_location"] == self.location_code:
                self.items[sku] -= qty
                if self.items[sku] == 0:
                    del self.items[sku]
            elif event.payload["to_location"] == self.location_code:
                self.items[sku] = self.items.get(sku, 0) + qty
        elif event.event_type == EventType.STOCK_SHIPPED:
            sku = event.aggregate_id
            qty = event.payload["quantity"]
            self.items[sku] -= qty
            if self.items[sku] == 0:
                del self.items[sku]
        self.version = event.aggregate_version

    def get_utilization(self) -> float:
        total = sum(self.items.values())
        return total / self.capacity * 100

# ==============================================================================
# 10.4.3 分布式锁:Redis Redlock模拟
# ==============================================================================
class DistributedLock:
    """分布式锁模拟(基于线程锁模拟Redis Redlock)"""

    def __init__(self):
        self._locks: Dict[str, threading.Lock] = defaultdict(threading.Lock)
        self._holders: Dict[str, str] = {}

    def acquire(self, resource: str, identifier: str, timeout: float = 10.0) -> bool:
        lock = self._locks[resource]
        acquired = lock.acquire(timeout=timeout)
        if acquired:
            self._holders[resource] = identifier
        return acquired

    def release(self, resource: str, identifier: str) -> bool:
        if self._holders.get(resource) == identifier:
            del self._holders[resource]
            try:
                self._locks[resource].release()
                return True
            except RuntimeError:
                return False
        return False

# ==============================================================================
# 10.4.4 Saga事务:跨聚合事务协调
# ==============================================================================
class SagaState(Enum):
    STARTED = auto()
    COMPENSATING = auto()
    COMPLETED = auto()
    FAILED = auto()

@dataclass
class SagaStep:
    name: str
    action: Callable
    compensation: Callable
    executed: bool = False

class SagaOrchestrator:
    """Saga编排器"""

    def __init__(self):
        self.sagas: Dict[str, Dict] = {}

    def start_saga(self, saga_id: str, steps: List[SagaStep]) -> bool:
        print(f"\n[启动Saga] {saga_id}")
        saga = {"id": saga_id, "state": SagaState.STARTED, "steps": steps, "current_step": 0}
        self.sagas[saga_id] = saga
        try:
            for i, step in enumerate(steps):
                saga["current_step"] = i
                print(f"  [步骤 {i+1}/{len(steps)}] {step.name}")
                step.action()
                step.executed = True
                print(f"  [完成] {step.name}")
            saga["state"] = SagaState.COMPLETED
            print(f"[Saga完成] {saga_id}")
            return True
        except Exception as e:
            print(f"  [失败] {e}")
            saga["state"] = SagaState.COMPENSATING
            self._compensate(saga)
            saga["state"] = SagaState.FAILED
            print(f"[Saga回滚] {saga_id}")
            return False

    def _compensate(self, saga: Dict):
        print("  [补偿] 开始执行补偿操作...")
        for i in range(saga["current_step"], -1, -1):
            step = saga["steps"][i]
            if step.executed:
                print(f"    [补偿步骤] {step.name}")
                try:
                    step.compensation()
                except Exception as e:
                    print(f"    [补偿失败] {e}")

# ==============================================================================
# 10.3.1 投影处理器:异步物化视图构建
# ==============================================================================
class ProjectionHandler:
    """投影处理器"""

    def __init__(self, event_store: EventStore):
        self.event_store = event_store
        self.projections: Dict[str, Any] = {}
        self.last_processed_id: Optional[int] = None
        self.processing_delay_ms: List[float] = []

    def build_current_stock_projection(self) -> Dict[str, Dict]:
        start_time = time.time()
        stock_view: Dict[str, Dict] = {}
        events = self.event_store.get_all_events()

        for event in events:
            sku = event.aggregate_id
            if sku not in stock_view:
                stock_view[sku] = {"sku": sku, "total": 0, "available": 0, "allocated": 0, "locations": defaultdict(int)}

            if event.event_type == EventType.STOCK_RECEIVED:
                stock_view[sku]["total"] += event.payload["quantity"]
                stock_view[sku]["available"] += event.payload["quantity"]
                stock_view[sku]["locations"][event.payload["location"]] += event.payload["quantity"]
            elif event.event_type == EventType.STOCK_ALLOCATED:
                stock_view[sku]["available"] -= event.payload["quantity"]
                stock_view[sku]["allocated"] += event.payload["quantity"]
            elif event.event_type == EventType.STOCK_SHIPPED:
                stock_view[sku]["total"] -= event.payload["quantity"]
                stock_view[sku]["allocated"] -= event.payload["quantity"]
                stock_view[sku]["locations"][event.payload["location"]] -= event.payload["quantity"]
            elif event.event_type == EventType.STOCK_MOVED:
                stock_view[sku]["locations"][event.payload["from_location"]] -= event.payload["quantity"]
                stock_view[sku]["locations"][event.payload["to_location"]] += event.payload["quantity"]
            elif event.event_type == EventType.STOCK_ADJUSTED:
                diff = event.payload["difference"]
                stock_view[sku]["total"] += diff
                stock_view[sku]["available"] += diff
                stock_view[sku]["locations"][event.payload["location"]] += diff

        delay = (time.time() - start_time) * 1000
        self.processing_delay_ms.append(delay)
        self.projections["current_stock"] = stock_view
        return stock_view

    def get_projection_delay_stats(self) -> Dict:
        if not self.processing_delay_ms:
            return {}
        return {
            "count": len(self.processing_delay_ms),
            "avg_ms": sum(self.processing_delay_ms) / len(self.processing_delay_ms),
            "max_ms": max(self.processing_delay_ms),
            "min_ms": min(self.processing_delay_ms)
        }

# ==============================================================================
# 10.3.3 历史追溯:任意时间点库存状态Time Travel查询
# ==============================================================================
class TimeTravelQuery:
    """时间旅行查询"""

    def __init__(self, event_store: EventStore):
        self.event_store = event_store

    def query_stock_at_time(self, sku: str, target_time: datetime) -> Dict:
        events = self.event_store.get_events(sku)
        filtered_events = [e for e in events if e.timestamp <= target_time]
        aggregate = ProductAggregate(sku)
        for event in filtered_events:
            aggregate.apply(event)
        return {
            "sku": sku,
            "query_time": target_time.isoformat(),
            "based_on_events": len(filtered_events),
            "total_quantity": aggregate.total_quantity,
            "available_quantity": aggregate.available_quantity,
            "allocated_quantity": aggregate.allocated_quantity,
            "locations": dict(aggregate.locations)
        }

# ==============================================================================
# 10.5.2 操作审计:用户行为全记录与合规报告
# ==============================================================================
class AuditLogger:
    """审计日志器"""

    def __init__(self, event_store: EventStore):
        self.event_store = event_store
        self.audit_entries: List[Dict] = []

    def log_operation(self, operation: str, user: str, details: Dict):
        entry = {
            "timestamp": datetime.now().isoformat(),
            "operation": operation,
            "user": user,
            "details": details,
            "trace_id": str(uuid.uuid4())
        }
        self.audit_entries.append(entry)

    def generate_compliance_report(self, start_time: datetime, end_time: datetime) -> Dict:
        events = self.event_store.get_all_events()
        filtered = [e for e in events if start_time <= e.timestamp <= end_time]
        report = {
            "report_period": f"{start_time.isoformat()} 至 {end_time.isoformat()}",
            "total_events": len(filtered),
            "event_breakdown": defaultdict(int),
            "operator_activity": defaultdict(int),
            "chain_integrity_verified": True
        }
        for event in filtered:
            report["event_breakdown"][event.event_type.value] += 1
            report["operator_activity"][event.payload.get("operator", "unknown")] += 1
        all_skus = set(e.aggregate_id for e in filtered)
        for sku in all_skus:
            if not self.event_store.verify_chain(sku):
                report["chain_integrity_verified"] = False
                break
        return dict(report)

# ==============================================================================
# 10.5.3 差异分析:物理盘点与系统数据差异报告
# ==============================================================================
class InventoryReconciliation:
    """库存对账与差异分析"""

    def __init__(self, projection_handler: ProjectionHandler):
        self.projection_handler = projection_handler

    def perform_reconciliation(self, physical_counts: Dict[str, Dict[str, int]]) -> Dict:
        stock_view = self.projection_handler.build_current_stock_projection()
        discrepancies = []
        total_skus = 0
        matching_skus = 0

        for sku, locations in physical_counts.items():
            total_skus += 1
            system_data = stock_view.get(sku, {"locations": {}})
            for location, physical_qty in locations.items():
                system_qty = system_data["locations"].get(location, 0)
                diff = physical_qty - system_qty
                if diff != 0:
                    discrepancies.append({
                        "sku": sku, "location": location,
                        "system_quantity": system_qty, "physical_quantity": physical_qty,
                        "difference": diff, "status": "盘盈" if diff > 0 else "盘亏"
                    })
                else:
                    matching_skus += 1

        return {
            "total_skus_checked": total_skus,
            "matching_skus": matching_skus,
            "discrepancy_count": len(discrepancies),
            "discrepancies": discrepancies,
            "accuracy_rate": (matching_skus / total_skus * 100) if total_skus > 0 else 0
        }

# ==============================================================================
# 仓储管理系统主类
# ==============================================================================
class EventSourcingWMS:
    """事件溯源仓储管理系统主类"""

    def __init__(self):
        print("=" * 80)
        print("事件溯源仓储管理系统(WMS)初始化中...")
        print("=" * 80)

        self.event_store = EventStore()
        self.projection_handler = ProjectionHandler(self.event_store)
        self.time_travel = TimeTravelQuery(self.event_store)
        self.audit_logger = AuditLogger(self.event_store)
        self.reconciliation = InventoryReconciliation(self.projection_handler)
        self.lock_manager = DistributedLock()
        self.saga_orchestrator = SagaOrchestrator()

        self.aggregates: Dict[str, ProductAggregate] = {}
        self.location_aggregates: Dict[str, LocationAggregate] = {}
        self.snapshots: Dict[str, Dict] = {}

        print("系统初始化完成\n")

    def get_or_create_aggregate(self, sku: str) -> ProductAggregate:
        if sku not in self.aggregates:
            if sku in self.snapshots:
                events = self.event_store.get_events(sku, self.snapshots[sku]["version"])
                self.aggregates[sku] = ProductAggregate.restore_from_snapshot(self.snapshots[sku], events)
                print(f"  [快照恢复] 聚合根 {sku} 从快照恢复(版本 {self.snapshots[sku]['version']})")
            else:
                self.aggregates[sku] = ProductAggregate(sku)
        return self.aggregates[sku]

    def get_or_create_location(self, location_code: str) -> LocationAggregate:
        if location_code not in self.location_aggregates:
            self.location_aggregates[location_code] = LocationAggregate(location_code)
        return self.location_aggregates[location_code]

    def save_aggregate(self, aggregate: ProductAggregate):
        for event in aggregate.pending_events:
            self.event_store.append(event)
            if event.event_type in [EventType.STOCK_RECEIVED, EventType.STOCK_MOVED, EventType.STOCK_SHIPPED]:
                if "location" in event.payload:
                    loc = self.get_or_create_location(event.payload["location"])
                    loc.apply(event)
                if event.event_type == EventType.STOCK_MOVED:
                    from_loc = self.get_or_create_location(event.payload["from_location"])
                    to_loc = self.get_or_create_location(event.payload["to_location"])
                    from_loc.apply(event)
                    to_loc.apply(event)
        aggregate.pending_events.clear()

        if aggregate.version > 0 and aggregate.version % 10 == 0:
            self.snapshots[aggregate.sku] = aggregate.create_snapshot()
            print(f"  [快照创建] 聚合根 {aggregate.sku} 快照已创建(版本 {aggregate.version})")

    def receive_stock(self, sku: str, quantity: int, location: str, batch_no: str):
        print(f"\n[入库] SKU={sku}, 数量={quantity}, 库位={location}")
        aggregate = self.get_or_create_aggregate(sku)
        event = aggregate.receive_stock(quantity, location, batch_no)
        self.save_aggregate(aggregate)
        self.audit_logger.log_operation("入库", "操作员A", {"sku": sku, "qty": quantity})
        print(f"  [完成] {sku} 当前库存 {aggregate.total_quantity}")
        return event

    def move_stock(self, sku: str, quantity: int, from_location: str, to_location: str):
        print(f"\n[移库] SKU={sku}, 数量={quantity}, {from_location} -> {to_location}")
        lock_id = f"move_{sku}_{from_location}"
        identifier = str(uuid.uuid4())
        if not self.lock_manager.acquire(lock_id, identifier, timeout=5.0):
            raise Exception("获取分布式锁失败,移库操作被拒绝")
        try:
            aggregate = self.get_or_create_aggregate(sku)
            event = aggregate.move_stock(quantity, from_location, to_location)
            self.save_aggregate(aggregate)
            self.audit_logger.log_operation("移库", "叉车工B", {"sku": sku, "qty": quantity, "from": from_location, "to": to_location})
            print(f"  [完成] {sku} 从 {from_location} 移至 {to_location}")
            return event
        finally:
            self.lock_manager.release(lock_id, identifier)

    def allocate_stock(self, sku: str, quantity: int, order_id: str):
        print(f"\n[分配] SKU={sku}, 数量={quantity}, 订单={order_id}")
        aggregate = self.get_or_create_aggregate(sku)
        event = aggregate.allocate_stock(quantity, order_id)
        self.save_aggregate(aggregate)
        self.audit_logger.log_operation("分配", "系统", {"sku": sku, "qty": quantity, "order": order_id})
        print(f"  [完成] {sku} 已分配 {aggregate.allocated_quantity}")
        return event

    def ship_stock(self, sku: str, quantity: int, location: str, order_id: str):
        print(f"\n[出库] SKU={sku}, 数量={quantity}, 订单={order_id}")
        aggregate = self.get_or_create_aggregate(sku)
        event = aggregate.ship_stock(quantity, location, order_id)
        self.save_aggregate(aggregate)
        self.audit_logger.log_operation("出库", "发货员C", {"sku": sku, "qty": quantity, "order": order_id})
        print(f"  [完成] {sku} 剩余库存 {aggregate.total_quantity}")
        return event

    def adjust_stock(self, sku: str, location: str, physical_qty: int):
        print(f"\n[盘点] SKU={sku}, 库位={location}, 实盘={physical_qty}")
        aggregate = self.get_or_create_aggregate(sku)
        event = aggregate.adjust_stock(location, physical_qty)
        self.save_aggregate(aggregate)
        self.audit_logger.log_operation("盘点调整", "盘点员D", {"sku": sku, "location": location, "physical": physical_qty})
        print(f"  [完成] {sku} 差异 {event.payload['difference']}")
        return event

    def run_saga_inbound_allocate(self, sku: str, quantity: int, location: str, order_id: str):
        print(f"\n[Saga] 启动入库-分配流程: SKU={sku}, 数量={quantity}")
        saga_id = f"saga_{snowflake.next_id()}"
        aggregate = self.get_or_create_aggregate(sku)

        steps = [
            SagaStep(name="入库", action=lambda: self._saga_receive(sku, quantity, location, f"BATCH_{saga_id}"),
                     compensation=lambda: print("    [补偿] 取消入库记录")),
            SagaStep(name="分配", action=lambda: self._saga_allocate(sku, quantity, order_id),
                     compensation=lambda: print("    [补偿] 释放分配库存"))
        ]
        return self.saga_orchestrator.start_saga(saga_id, steps)

    def _saga_receive(self, sku, qty, loc, batch):
        aggregate = self.get_or_create_aggregate(sku)
        event = aggregate.receive_stock(qty, loc, batch)
        self.save_aggregate(aggregate)

    def _saga_allocate(self, sku, qty, order):
        aggregate = self.get_or_create_aggregate(sku)
        event = aggregate.allocate_stock(qty, order)
        self.save_aggregate(aggregate)

    def query_current_stock(self) -> Dict:
        print("\n[投影] 构建当前库存物化视图...")
        return self.projection_handler.build_current_stock_projection()

    def time_travel_query(self, sku: str, target_time: datetime) -> Dict:
        print(f"\n[TimeTravel] SKU={sku}, 时间点={target_time.isoformat()}")
        return self.time_travel.query_stock_at_time(sku, target_time)

    def replay_events(self, sku: str):
        print(f"\n[回放] 事件回放: SKU={sku}")
        events = self.event_store.get_events(sku)
        print(f"  共 {len(events)} 个事件,开始重放...")
        temp_aggregate = ProductAggregate(sku)
        for i, event in enumerate(events, 1):
            print(f"    [{i}/{len(events)}] {event.event_type.value} (v{event.aggregate_version}) - {event.timestamp.strftime('%H:%M:%S')}")
            temp_aggregate.apply(event)
        print(f"  [回放完成] 总库存={temp_aggregate.total_quantity}, 可用={temp_aggregate.available_quantity}, 已分配={temp_aggregate.allocated_quantity}")
        return temp_aggregate

    def generate_audit_report(self, hours: int = 24) -> Dict:
        print(f"\n[审计] 生成过去{hours}小时合规审计报告...")
        end_time = datetime.now()
        start_time = end_time - timedelta(hours=hours)
        return self.audit_logger.generate_compliance_report(start_time, end_time)

    def perform_physical_count(self, physical_counts: Dict[str, Dict[str, int]]) -> Dict:
        print("\n[对账] 执行物理盘点与系统数据差异分析...")
        return self.reconciliation.perform_reconciliation(physical_counts)

    def demonstrate_concurrency_control(self):
        print("\n[并发控制演示] 乐观并发控制(版本号冲突检测)...")
        sku = "SKU-TEST-CONCURRENT"
        aggregate = self.get_or_create_aggregate(sku)
        aggregate.receive_stock(100, "A01", "BATCH_INIT")
        self.save_aggregate(aggregate)

        # 模拟两个并发操作:从事件流重建两个独立的聚合根实例
        agg1 = ProductAggregate(sku)
        for ev in self.event_store.get_events(sku):
            agg1.apply(ev)

        agg2 = ProductAggregate(sku)
        for ev in self.event_store.get_events(sku):
            agg2.apply(ev)

        # 操作1:移库10个并保存
        event1 = agg1.move_stock(10, "A01", "A02")
        self.save_aggregate(agg1)

        # 操作2:分配20个(基于相同初始版本,应因版本冲突失败)
        try:
            event2 = agg2.allocate_stock(20, "ORDER_001")
            self.save_aggregate(agg2)
        except ConcurrentModificationException as e:
            print(f"  [检测成功] 并发冲突被正确检测: {e}")
            print("  [解决方案] 重试策略 - 重新加载聚合根后重试操作")

    def demonstrate_idempotency(self):
        print("\n[幂等性演示] 基于事件ID去重...")
        sku = "SKU-TEST-IDEMPOTENT"
        aggregate = self.get_or_create_aggregate(sku)
        event = aggregate.receive_stock(50, "B01", "BATCH_001")
        self.save_aggregate(aggregate)

        print(f"  尝试重复提交事件 ID={event.event_id}...")
        result = self.event_store.append(event)
        if not result:
            print("  [拦截成功] 幂等性保障:重复事件被拒绝")

    def print_event_store_stats(self):
        print("\n[统计] 事件存储统计信息:")
        all_events = self.event_store.get_all_events()
        print(f"  全局事件总数: {len(all_events)}")
        print(f"  聚合根数量: {len(self.aggregates)}")
        event_types = defaultdict(int)
        for e in all_events:
            event_types[e.event_type.value] += 1
        print("  事件类型分布:")
        for et, count in sorted(event_types.items()):
            print(f"    - {et}: {count}")
        delay_stats = self.projection_handler.get_projection_delay_stats()
        if delay_stats:
            print(f"  投影处理延迟: 平均={delay_stats['avg_ms']:.2f}ms, 最大={delay_stats['max_ms']:.2f}ms")

    def print_separator(self, title: str):
        print("\n" + "=" * 80)
        print(f"  {title}")
        print("=" * 80)


# ==============================================================================
# 主程序:完整仿真流程
# ==============================================================================
def main():
    wms = EventSourcingWMS()

    # 阶段一:基础业务操作
    wms.print_separator("阶段一:基础业务操作演示(10.1 领域建模)")
    wms.receive_stock("SKU-001", 1000, "A01-01", "BATCH-20260527-001")
    wms.receive_stock("SKU-001", 500, "A01-02", "BATCH-20260527-002")
    wms.receive_stock("SKU-002", 800, "B02-01", "BATCH-20260527-003")
    wms.move_stock("SKU-001", 200, "A01-01", "A01-03")
    wms.allocate_stock("SKU-001", 300, "ORDER-20260527-001")
    wms.allocate_stock("SKU-002", 150, "ORDER-20260527-002")
    wms.ship_stock("SKU-001", 100, "A01-01", "ORDER-20260527-001")

    # 阶段二:Saga事务
    wms.print_separator("阶段二:Saga跨聚合事务协调(10.4.4)")
    wms.run_saga_inbound_allocate("SKU-003", 500, "C03-01", "ORDER-SAGA-001")

    # 阶段三:并发控制与幂等性
    wms.print_separator("阶段三:并发控制与幂等性(10.4.1 / 10.4.2)")
    wms.demonstrate_concurrency_control()
    wms.demonstrate_idempotency()

    # 阶段四:投影与查询
    wms.print_separator("阶段四:投影与查询(10.3 投影与查询)")
    stock_view = wms.query_current_stock()
    print("\n[当前库存视图]")
    for sku, data in stock_view.items():
        print(f"  SKU: {sku}")
        print(f"    总库存: {data['total']}")
        print(f"    可用库存: {data['available']}")
        print(f"    已分配: {data['allocated']}")
        print(f"    库位分布: {dict(data['locations'])}")

    past_time = datetime.now() - timedelta(minutes=5)
    travel_result = wms.time_travel_query("SKU-001", past_time)
    print(f"\n[TimeTravel结果]")
    print(f"  查询时间: {travel_result['query_time']}")
    print(f"  基于事件数: {travel_result['based_on_events']}")
    print(f"  当时总库存: {travel_result['total_quantity']}")
    print(f"  当时可用库存: {travel_result['available_quantity']}")

    wms.replay_events("SKU-001")

    # 阶段五:盘点与差异分析
    wms.print_separator("阶段五:盘点与差异分析(10.5.3)")
    physical_counts = {
        "SKU-001": {"A01-01": 700, "A01-02": 500, "A01-03": 180},
        "SKU-002": {"B02-01": 850}
    }
    reconciliation = wms.perform_physical_count(physical_counts)
    print(f"\n[盘点对账结果]")
    print(f"  检查SKU数: {reconciliation['total_skus_checked']}")
    print(f"  匹配数: {reconciliation['matching_skus']}")
    print(f"  差异数: {reconciliation['discrepancy_count']}")
    print(f"  准确率: {reconciliation['accuracy_rate']:.1f}%")
    if reconciliation['discrepancies']:
        print(f"\n  差异明细:")
        for disc in reconciliation['discrepancies']:
            print(f"    [差异] {disc['sku']} @ {disc['location']}: 系统={disc['system_quantity']}, 实盘={disc['physical_quantity']}, 差异={disc['difference']} ({disc['status']})")

    # 阶段六:审计与合规
    wms.print_separator("阶段六:审计与合规(10.5.1 / 10.5.2)")
    audit_report = wms.generate_audit_report(hours=1)
    print(f"\n[合规审计报告]")
    print(f"  报告周期: {audit_report['report_period']}")
    print(f"  事件总数: {audit_report['total_events']}")
    print(f"  事件分布: {dict(audit_report['event_breakdown'])}")
    print(f"  操作员活动: {dict(audit_report['operator_activity'])}")
    print(f"  链完整性验证: {'通过' if audit_report['chain_integrity_verified'] else '失败'}")

    print(f"\n[区块链式事件链验证]")
    for sku in wms.aggregates.keys():
        valid = wms.event_store.verify_chain(sku)
        print(f"  {sku}: {'哈希链完整' if valid else '哈希链断裂'}")

    # 阶段七:性能统计
    wms.print_separator("阶段七:系统统计与总结(10.5.4)")
    wms.print_event_store_stats()

    print(f"\n[库位利用率]")
    for loc_code, loc_agg in wms.location_aggregates.items():
        util = loc_agg.get_utilization()
        items = dict(loc_agg.items)
        print(f"  {loc_code}: 利用率 {util:.1f}%, 存放商品: {items}")

    print(f"\n[序列化演示]")
    all_events = wms.event_store.get_all_events()
    if all_events:
        sample_event = all_events[0]
        serialized = EventSerializer.serialize(sample_event)
        print(f"  原始事件: {sample_event.event_type.value} (ID={sample_event.event_id})")
        print(f"  序列化大小: {len(serialized)} 字节")
        print(f"  Schema版本: {EventSerializer.SCHEMA_VERSION}")
        deserialized = EventSerializer.deserialize(serialized)
        print(f"  反序列化验证: {'成功' if deserialized.event_id == sample_event.event_id else '失败'}")

    print("\n" + "=" * 80)
    print("事件溯源仓储管理系统仿真完成!")
    print("=" * 80)


if __name__ == "__main__":
    main()

目录

  1. 系统架构概览
  2. 完整代码实现
  3. 运行说明
  4. 输出示例
  5. 核心设计要点总结

1. 系统架构概览

本仿真系统完整实现了以下核心模块:

章节 模块 实现内容
10.1 领域建模 StockReceived/StockMoved/StockAllocated事件、ProductAggregate/LocationAggregate聚合根、快照机制、版本号并发控制
10.2 事件存储 内存事件存储引擎(模拟PostgreSQL JSONB)、Protobuf序列化模拟、Snowflake ID、归档策略
10.3 投影与查询 异步投影处理器、物化视图、Time Travel查询、事件回放
10.4 并发与一致性 乐观并发控制、幂等性去重、Redis Redlock模拟、Saga事务协调
10.5 审计与合规 区块链式哈希链、操作审计日志、差异分析、批量处理与延迟监控

2. 完整代码实现

将以下代码保存为 wms_event_sourcing.py,直接运行即可。

python 复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
================================================================================
项目十:事件溯源仓储管理系统(WMS)仿真
基于Event Sourcing与CQRS的库存精准管理系统
================================================================================
作者:AI Assistant
日期:2026-05-27
说明:本程序为教学仿真,使用内存存储模拟完整的事件溯源架构
================================================================================
"""

import json
import hashlib
import time
import random
import threading
import uuid
from datetime import datetime, timedelta
from dataclasses import dataclass, field, asdict
from typing import Dict, List, Optional, Callable, Any, Set
from enum import Enum, auto
from collections import defaultdict
import copy

# ==============================================================================
# 10.2.3 全局排序:Snowflake ID 实现
# ==============================================================================
class SnowflakeGenerator:
    """Snowflake ID生成器:保证全局唯一且有序"""

    def __init__(self, datacenter_id: int = 0, worker_id: int = 0):
        self.datacenter_id = datacenter_id
        self.worker_id = worker_id
        self.sequence = 0
        self.last_timestamp = -1
        self.twepoch = 1609459200000
        self.datacenter_id_bits = 5
        self.worker_id_bits = 5
        self.sequence_bits = 12
        self.max_datacenter_id = -1 ^ (-1 << self.datacenter_id_bits)
        self.max_worker_id = -1 ^ (-1 << self.worker_id_bits)
        self.sequence_mask = -1 ^ (-1 << self.sequence_bits)
        self.worker_id_shift = self.sequence_bits
        self.datacenter_id_shift = self.sequence_bits + self.worker_id_bits
        self.timestamp_left_shift = self.sequence_bits + self.worker_id_bits + self.datacenter_id_bits
        self.lock = threading.Lock()

    def _current_timestamp(self) -> int:
        return int(time.time() * 1000)

    def _til_next_millis(self, last_timestamp: int) -> int:
        timestamp = self._current_timestamp()
        while timestamp <= last_timestamp:
            timestamp = self._current_timestamp()
        return timestamp

    def next_id(self) -> int:
        with self.lock:
            timestamp = self._current_timestamp()
            if timestamp < self.last_timestamp:
                raise Exception("时钟回拨异常")
            if timestamp == self.last_timestamp:
                self.sequence = (self.sequence + 1) & self.sequence_mask
                if self.sequence == 0:
                    timestamp = self._til_next_millis(self.last_timestamp)
            else:
                self.sequence = 0
            self.last_timestamp = timestamp
            return ((timestamp - self.twepoch) << self.timestamp_left_shift) | \
                   (self.datacenter_id << self.datacenter_id_shift) | \
                   (self.worker_id << self.worker_id_shift) | self.sequence

snowflake = SnowflakeGenerator()

# ==============================================================================
# 10.1.1 库存事件设计:领域事件定义
# ==============================================================================
class EventType(Enum):
    """事件类型枚举"""
    STOCK_RECEIVED = "StockReceived"
    STOCK_MOVED = "StockMoved"
    STOCK_ALLOCATED = "StockAllocated"
    STOCK_SHIPPED = "StockShipped"
    STOCK_ADJUSTED = "StockAdjusted"

@dataclass
class DomainEvent:
    """领域事件基类"""
    event_id: int
    event_type: EventType
    aggregate_id: str
    aggregate_version: int
    timestamp: datetime
    payload: Dict[str, Any]
    previous_hash: str = ""
    event_hash: str = ""

    def compute_hash(self) -> str:
        """计算事件哈希:实现10.5.1不可篡改日志"""
        data = {
            "event_id": self.event_id,
            "event_type": self.event_type.value,
            "aggregate_id": self.aggregate_id,
            "aggregate_version": self.aggregate_version,
            "timestamp": self.timestamp.isoformat(),
            "payload": self.payload,
            "previous_hash": self.previous_hash
        }
        return hashlib.sha256(json.dumps(data, sort_keys=True, ensure_ascii=False).encode()).hexdigest()

    def finalize(self):
        """最终化事件:计算哈希"""
        self.event_hash = self.compute_hash()

# ==============================================================================
# 10.2.2 事件序列化:Protobuf模拟(使用JSON+Schema版本管理)
# ==============================================================================
class EventSerializer:
    """事件序列化器:模拟Protobuf二进制格式与Schema版本管理"""

    SCHEMA_VERSION = "1.0.0"

    @staticmethod
    def serialize(event: DomainEvent) -> bytes:
        """序列化事件为二进制(模拟Protobuf)"""
        data = {
            "schema_version": EventSerializer.SCHEMA_VERSION,
            "event_id": event.event_id,
            "event_type": event.event_type.value,
            "aggregate_id": event.aggregate_id,
            "aggregate_version": event.aggregate_version,
            "timestamp": event.timestamp.isoformat(),
            "payload": event.payload,
            "previous_hash": event.previous_hash,
            "event_hash": event.event_hash
        }
        return json.dumps(data, ensure_ascii=False).encode("utf-8")

    @staticmethod
    def deserialize(data: bytes) -> DomainEvent:
        """反序列化事件"""
        obj = json.loads(data.decode("utf-8"))
        event = DomainEvent(
            event_id=obj["event_id"],
            event_type=EventType(obj["event_type"]),
            aggregate_id=obj["aggregate_id"],
            aggregate_version=obj["aggregate_version"],
            timestamp=datetime.fromisoformat(obj["timestamp"]),
            payload=obj["payload"],
            previous_hash=obj["previous_hash"],
            event_hash=obj["event_hash"]
        )
        return event

# ==============================================================================
# 10.2.1 事件存储:内存事件存储引擎(模拟PostgreSQL JSONB)
# 10.2.4 归档策略:历史事件冷存储与压缩
# ==============================================================================
class EventStore:
    """事件存储引擎:追加写模型与版本号并发控制"""

    def __init__(self):
        self._streams: Dict[str, List[DomainEvent]] = defaultdict(list)
        self._global_events: Dict[int, DomainEvent] = {}
        self._processed_ids: Set[int] = set()
        self._archived: Dict[str, List[DomainEvent]] = defaultdict(list)
        self._archive_threshold = 50
        self._lock = threading.RLock()
        self._last_hashes: Dict[str, str] = {}

    def append(self, event: DomainEvent) -> bool:
        """追加事件:实现乐观并发控制"""
        with self._lock:
            # 10.4.2 幂等性保障
            if event.event_id in self._processed_ids:
                print(f"  [幂等性拦截] 事件 {event.event_id} 已存在,跳过处理")
                return False

            stream = self._streams[event.aggregate_id]

            # 10.4.1 乐观并发控制
            expected_version = len(stream)
            if event.aggregate_version != expected_version + 1:
                raise ConcurrentModificationException(
                    f"并发冲突:聚合根 {event.aggregate_id} 期望版本 {expected_version + 1},"
                    f"实际版本 {event.aggregate_version}"
                )

            # 10.5.1 不可篡改日志:区块链式事件链
            event.previous_hash = self._last_hashes.get(event.aggregate_id, "0" * 64)
            event.finalize()
            self._last_hashes[event.aggregate_id] = event.event_hash

            stream.append(event)
            self._global_events[event.event_id] = event
            self._processed_ids.add(event.event_id)

            # 10.2.4 归档策略检查
            self._check_archive(event.aggregate_id)
            return True

    def _check_archive(self, aggregate_id: str):
        """检查并执行归档"""
        stream = self._streams[aggregate_id]
        if len(stream) > self._archive_threshold:
            archive_count = len(stream) // 2
            archived_events = stream[:archive_count]
            self._archived[aggregate_id].extend(archived_events)
            self._streams[aggregate_id] = stream[archive_count:]
            print(f"  [归档] 聚合根 {aggregate_id} 的 {archive_count} 个事件已归档(冷存储)")

    def get_events(self, aggregate_id: str, from_version: int = 0) -> List[DomainEvent]:
        """获取聚合根事件流"""
        with self._lock:
            archived = self._archived.get(aggregate_id, [])
            active = self._streams.get(aggregate_id, [])
            all_events = archived + active
            return all_events[from_version:]

    def get_all_events(self) -> List[DomainEvent]:
        """获取所有事件(按全局顺序)"""
        with self._lock:
            return sorted(self._global_events.values(), key=lambda e: e.event_id)

    def get_event_by_id(self, event_id: int) -> Optional[DomainEvent]:
        return self._global_events.get(event_id)

    def verify_chain(self, aggregate_id: str) -> bool:
        """验证事件链完整性"""
        events = self.get_events(aggregate_id)
        for i, event in enumerate(events):
            if i == 0:
                continue
            if event.previous_hash != events[i-1].event_hash:
                return False
            if event.compute_hash() != event.event_hash:
                return False
        return True

class ConcurrentModificationException(Exception):
    """并发修改异常"""
    pass

# ==============================================================================
# 10.1.2 聚合根实现:ProductAggregate与LocationAggregate
# 10.1.3 快照机制
# ==============================================================================
class ProductAggregate:
    """商品聚合根:管理单个SKU的库存状态"""

    def __init__(self, sku: str):
        self.sku = sku
        self.version = 0
        self.total_quantity = 0
        self.available_quantity = 0
        self.allocated_quantity = 0
        self.locations: Dict[str, int] = {}
        self.pending_events: List[DomainEvent] = []
        self._snapshot_version = 0

    def apply(self, event: DomainEvent):
        """应用事件到聚合根状态"""
        if event.event_type == EventType.STOCK_RECEIVED:
            self._on_stock_received(event)
        elif event.event_type == EventType.STOCK_MOVED:
            self._on_stock_moved(event)
        elif event.event_type == EventType.STOCK_ALLOCATED:
            self._on_stock_allocated(event)
        elif event.event_type == EventType.STOCK_SHIPPED:
            self._on_stock_shipped(event)
        elif event.event_type == EventType.STOCK_ADJUSTED:
            self._on_stock_adjusted(event)
        self.version = event.aggregate_version

    def _on_stock_received(self, event: DomainEvent):
        qty = event.payload["quantity"]
        location = event.payload["location"]
        self.total_quantity += qty
        self.available_quantity += qty
        self.locations[location] = self.locations.get(location, 0) + qty

    def _on_stock_moved(self, event: DomainEvent):
        qty = event.payload["quantity"]
        from_loc = event.payload["from_location"]
        to_loc = event.payload["to_location"]
        self.locations[from_loc] -= qty
        self.locations[to_loc] = self.locations.get(to_loc, 0) + qty
        if self.locations[from_loc] == 0:
            del self.locations[from_loc]

    def _on_stock_allocated(self, event: DomainEvent):
        qty = event.payload["quantity"]
        self.available_quantity -= qty
        self.allocated_quantity += qty

    def _on_stock_shipped(self, event: DomainEvent):
        qty = event.payload["quantity"]
        location = event.payload["location"]
        self.total_quantity -= qty
        self.allocated_quantity -= qty
        self.locations[location] -= qty
        if self.locations[location] == 0:
            del self.locations[location]

    def _on_stock_adjusted(self, event: DomainEvent):
        diff = event.payload["difference"]
        location = event.payload["location"]
        self.total_quantity += diff
        self.available_quantity += diff
        self.locations[location] = self.locations.get(location, 0) + diff

    def receive_stock(self, quantity: int, location: str, batch_no: str) -> DomainEvent:
        """入库命令"""
        self.version += 1
        event = DomainEvent(
            event_id=snowflake.next_id(),
            event_type=EventType.STOCK_RECEIVED,
            aggregate_id=self.sku,
            aggregate_version=self.version,
            timestamp=datetime.now(),
            payload={"quantity": quantity, "location": location, "batch_no": batch_no, "operator": "操作员A"}
        )
        self.apply(event)
        self.pending_events.append(event)
        return event

    def move_stock(self, quantity: int, from_location: str, to_location: str) -> DomainEvent:
        """移库命令"""
        if self.locations.get(from_location, 0) < quantity:
            raise ValueError(f"库位 {from_location} 库存不足")
        self.version += 1
        event = DomainEvent(
            event_id=snowflake.next_id(),
            event_type=EventType.STOCK_MOVED,
            aggregate_id=self.sku,
            aggregate_version=self.version,
            timestamp=datetime.now(),
            payload={"quantity": quantity, "from_location": from_location, "to_location": to_location, "operator": "叉车工B"}
        )
        self.apply(event)
        self.pending_events.append(event)
        return event

    def allocate_stock(self, quantity: int, order_id: str) -> DomainEvent:
        """分配命令"""
        if self.available_quantity < quantity:
            raise ValueError(f"可用库存不足:需要 {quantity},可用 {self.available_quantity}")
        self.version += 1
        event = DomainEvent(
            event_id=snowflake.next_id(),
            event_type=EventType.STOCK_ALLOCATED,
            aggregate_id=self.sku,
            aggregate_version=self.version,
            timestamp=datetime.now(),
            payload={"quantity": quantity, "order_id": order_id, "operator": "系统"}
        )
        self.apply(event)
        self.pending_events.append(event)
        return event

    def ship_stock(self, quantity: int, location: str, order_id: str) -> DomainEvent:
        """出库命令"""
        self.version += 1
        event = DomainEvent(
            event_id=snowflake.next_id(),
            event_type=EventType.STOCK_SHIPPED,
            aggregate_id=self.sku,
            aggregate_version=self.version,
            timestamp=datetime.now(),
            payload={"quantity": quantity, "location": location, "order_id": order_id, "operator": "发货员C"}
        )
        self.apply(event)
        self.pending_events.append(event)
        return event

    def adjust_stock(self, location: str, physical_qty: int) -> DomainEvent:
        """盘点调整命令"""
        system_qty = self.locations.get(location, 0)
        diff = physical_qty - system_qty
        self.version += 1
        event = DomainEvent(
            event_id=snowflake.next_id(),
            event_type=EventType.STOCK_ADJUSTED,
            aggregate_id=self.sku,
            aggregate_version=self.version,
            timestamp=datetime.now(),
            payload={"location": location, "system_quantity": system_qty, "physical_quantity": physical_qty, "difference": diff, "operator": "盘点员D"}
        )
        self.apply(event)
        self.pending_events.append(event)
        return event

    def create_snapshot(self) -> Dict:
        """创建快照"""
        return {
            "sku": self.sku,
            "version": self.version,
            "total_quantity": self.total_quantity,
            "available_quantity": self.available_quantity,
            "allocated_quantity": self.allocated_quantity,
            "locations": copy.deepcopy(self.locations),
            "timestamp": datetime.now().isoformat()
        }

    @classmethod
    def restore_from_snapshot(cls, snapshot: Dict, events: List[DomainEvent]) -> "ProductAggregate":
        """从快照恢复聚合根"""
        aggregate = cls(snapshot["sku"])
        aggregate.version = snapshot["version"]
        aggregate.total_quantity = snapshot["total_quantity"]
        aggregate.available_quantity = snapshot["available_quantity"]
        aggregate.allocated_quantity = snapshot["allocated_quantity"]
        aggregate.locations = copy.deepcopy(snapshot["locations"])
        aggregate._snapshot_version = snapshot["version"]
        for event in events:
            aggregate.apply(event)
        return aggregate

class LocationAggregate:
    """库位聚合根"""

    def __init__(self, location_code: str):
        self.location_code = location_code
        self.version = 0
        self.items: Dict[str, int] = {}
        self.capacity = 1000
        self.pending_events: List[DomainEvent] = []

    def apply(self, event: DomainEvent):
        if event.event_type == EventType.STOCK_RECEIVED:
            sku = event.aggregate_id
            qty = event.payload["quantity"]
            self.items[sku] = self.items.get(sku, 0) + qty
        elif event.event_type == EventType.STOCK_MOVED:
            sku = event.aggregate_id
            qty = event.payload["quantity"]
            if event.payload["from_location"] == self.location_code:
                self.items[sku] -= qty
                if self.items[sku] == 0:
                    del self.items[sku]
            elif event.payload["to_location"] == self.location_code:
                self.items[sku] = self.items.get(sku, 0) + qty
        elif event.event_type == EventType.STOCK_SHIPPED:
            sku = event.aggregate_id
            qty = event.payload["quantity"]
            self.items[sku] -= qty
            if self.items[sku] == 0:
                del self.items[sku]
        self.version = event.aggregate_version

    def get_utilization(self) -> float:
        total = sum(self.items.values())
        return total / self.capacity * 100

# ==============================================================================
# 10.4.3 分布式锁:Redis Redlock模拟
# ==============================================================================
class DistributedLock:
    """分布式锁模拟(基于线程锁模拟Redis Redlock)"""

    def __init__(self):
        self._locks: Dict[str, threading.Lock] = defaultdict(threading.Lock)
        self._holders: Dict[str, str] = {}

    def acquire(self, resource: str, identifier: str, timeout: float = 10.0) -> bool:
        lock = self._locks[resource]
        acquired = lock.acquire(timeout=timeout)
        if acquired:
            self._holders[resource] = identifier
        return acquired

    def release(self, resource: str, identifier: str) -> bool:
        if self._holders.get(resource) == identifier:
            del self._holders[resource]
            try:
                self._locks[resource].release()
                return True
            except RuntimeError:
                return False
        return False

# ==============================================================================
# 10.4.4 Saga事务:跨聚合事务协调
# ==============================================================================
class SagaState(Enum):
    STARTED = auto()
    COMPENSATING = auto()
    COMPLETED = auto()
    FAILED = auto()

@dataclass
class SagaStep:
    name: str
    action: Callable
    compensation: Callable
    executed: bool = False

class SagaOrchestrator:
    """Saga编排器"""

    def __init__(self):
        self.sagas: Dict[str, Dict] = {}

    def start_saga(self, saga_id: str, steps: List[SagaStep]) -> bool:
        print(f"\n[启动Saga] {saga_id}")
        saga = {"id": saga_id, "state": SagaState.STARTED, "steps": steps, "current_step": 0}
        self.sagas[saga_id] = saga
        try:
            for i, step in enumerate(steps):
                saga["current_step"] = i
                print(f"  [步骤 {i+1}/{len(steps)}] {step.name}")
                step.action()
                step.executed = True
                print(f"  [完成] {step.name}")
            saga["state"] = SagaState.COMPLETED
            print(f"[Saga完成] {saga_id}")
            return True
        except Exception as e:
            print(f"  [失败] {e}")
            saga["state"] = SagaState.COMPENSATING
            self._compensate(saga)
            saga["state"] = SagaState.FAILED
            print(f"[Saga回滚] {saga_id}")
            return False

    def _compensate(self, saga: Dict):
        print("  [补偿] 开始执行补偿操作...")
        for i in range(saga["current_step"], -1, -1):
            step = saga["steps"][i]
            if step.executed:
                print(f"    [补偿步骤] {step.name}")
                try:
                    step.compensation()
                except Exception as e:
                    print(f"    [补偿失败] {e}")

# ==============================================================================
# 10.3.1 投影处理器:异步物化视图构建
# ==============================================================================
class ProjectionHandler:
    """投影处理器"""

    def __init__(self, event_store: EventStore):
        self.event_store = event_store
        self.projections: Dict[str, Any] = {}
        self.last_processed_id: Optional[int] = None
        self.processing_delay_ms: List[float] = []

    def build_current_stock_projection(self) -> Dict[str, Dict]:
        start_time = time.time()
        stock_view: Dict[str, Dict] = {}
        events = self.event_store.get_all_events()

        for event in events:
            sku = event.aggregate_id
            if sku not in stock_view:
                stock_view[sku] = {"sku": sku, "total": 0, "available": 0, "allocated": 0, "locations": defaultdict(int)}

            if event.event_type == EventType.STOCK_RECEIVED:
                stock_view[sku]["total"] += event.payload["quantity"]
                stock_view[sku]["available"] += event.payload["quantity"]
                stock_view[sku]["locations"][event.payload["location"]] += event.payload["quantity"]
            elif event.event_type == EventType.STOCK_ALLOCATED:
                stock_view[sku]["available"] -= event.payload["quantity"]
                stock_view[sku]["allocated"] += event.payload["quantity"]
            elif event.event_type == EventType.STOCK_SHIPPED:
                stock_view[sku]["total"] -= event.payload["quantity"]
                stock_view[sku]["allocated"] -= event.payload["quantity"]
                stock_view[sku]["locations"][event.payload["location"]] -= event.payload["quantity"]
            elif event.event_type == EventType.STOCK_MOVED:
                stock_view[sku]["locations"][event.payload["from_location"]] -= event.payload["quantity"]
                stock_view[sku]["locations"][event.payload["to_location"]] += event.payload["quantity"]
            elif event.event_type == EventType.STOCK_ADJUSTED:
                diff = event.payload["difference"]
                stock_view[sku]["total"] += diff
                stock_view[sku]["available"] += diff
                stock_view[sku]["locations"][event.payload["location"]] += diff

        delay = (time.time() - start_time) * 1000
        self.processing_delay_ms.append(delay)
        self.projections["current_stock"] = stock_view
        return stock_view

    def get_projection_delay_stats(self) -> Dict:
        if not self.processing_delay_ms:
            return {}
        return {
            "count": len(self.processing_delay_ms),
            "avg_ms": sum(self.processing_delay_ms) / len(self.processing_delay_ms),
            "max_ms": max(self.processing_delay_ms),
            "min_ms": min(self.processing_delay_ms)
        }

# ==============================================================================
# 10.3.3 历史追溯:任意时间点库存状态Time Travel查询
# ==============================================================================
class TimeTravelQuery:
    """时间旅行查询"""

    def __init__(self, event_store: EventStore):
        self.event_store = event_store

    def query_stock_at_time(self, sku: str, target_time: datetime) -> Dict:
        events = self.event_store.get_events(sku)
        filtered_events = [e for e in events if e.timestamp <= target_time]
        aggregate = ProductAggregate(sku)
        for event in filtered_events:
            aggregate.apply(event)
        return {
            "sku": sku,
            "query_time": target_time.isoformat(),
            "based_on_events": len(filtered_events),
            "total_quantity": aggregate.total_quantity,
            "available_quantity": aggregate.available_quantity,
            "allocated_quantity": aggregate.allocated_quantity,
            "locations": dict(aggregate.locations)
        }

# ==============================================================================
# 10.5.2 操作审计:用户行为全记录与合规报告
# ==============================================================================
class AuditLogger:
    """审计日志器"""

    def __init__(self, event_store: EventStore):
        self.event_store = event_store
        self.audit_entries: List[Dict] = []

    def log_operation(self, operation: str, user: str, details: Dict):
        entry = {
            "timestamp": datetime.now().isoformat(),
            "operation": operation,
            "user": user,
            "details": details,
            "trace_id": str(uuid.uuid4())
        }
        self.audit_entries.append(entry)

    def generate_compliance_report(self, start_time: datetime, end_time: datetime) -> Dict:
        events = self.event_store.get_all_events()
        filtered = [e for e in events if start_time <= e.timestamp <= end_time]
        report = {
            "report_period": f"{start_time.isoformat()} 至 {end_time.isoformat()}",
            "total_events": len(filtered),
            "event_breakdown": defaultdict(int),
            "operator_activity": defaultdict(int),
            "chain_integrity_verified": True
        }
        for event in filtered:
            report["event_breakdown"][event.event_type.value] += 1
            report["operator_activity"][event.payload.get("operator", "unknown")] += 1
        all_skus = set(e.aggregate_id for e in filtered)
        for sku in all_skus:
            if not self.event_store.verify_chain(sku):
                report["chain_integrity_verified"] = False
                break
        return dict(report)

# ==============================================================================
# 10.5.3 差异分析:物理盘点与系统数据差异报告
# ==============================================================================
class InventoryReconciliation:
    """库存对账与差异分析"""

    def __init__(self, projection_handler: ProjectionHandler):
        self.projection_handler = projection_handler

    def perform_reconciliation(self, physical_counts: Dict[str, Dict[str, int]]) -> Dict:
        stock_view = self.projection_handler.build_current_stock_projection()
        discrepancies = []
        total_skus = 0
        matching_skus = 0

        for sku, locations in physical_counts.items():
            total_skus += 1
            system_data = stock_view.get(sku, {"locations": {}})
            for location, physical_qty in locations.items():
                system_qty = system_data["locations"].get(location, 0)
                diff = physical_qty - system_qty
                if diff != 0:
                    discrepancies.append({
                        "sku": sku, "location": location,
                        "system_quantity": system_qty, "physical_quantity": physical_qty,
                        "difference": diff, "status": "盘盈" if diff > 0 else "盘亏"
                    })
                else:
                    matching_skus += 1

        return {
            "total_skus_checked": total_skus,
            "matching_skus": matching_skus,
            "discrepancy_count": len(discrepancies),
            "discrepancies": discrepancies,
            "accuracy_rate": (matching_skus / total_skus * 100) if total_skus > 0 else 0
        }

# ==============================================================================
# 仓储管理系统主类
# ==============================================================================
class EventSourcingWMS:
    """事件溯源仓储管理系统主类"""

    def __init__(self):
        print("=" * 80)
        print("事件溯源仓储管理系统(WMS)初始化中...")
        print("=" * 80)

        self.event_store = EventStore()
        self.projection_handler = ProjectionHandler(self.event_store)
        self.time_travel = TimeTravelQuery(self.event_store)
        self.audit_logger = AuditLogger(self.event_store)
        self.reconciliation = InventoryReconciliation(self.projection_handler)
        self.lock_manager = DistributedLock()
        self.saga_orchestrator = SagaOrchestrator()

        self.aggregates: Dict[str, ProductAggregate] = {}
        self.location_aggregates: Dict[str, LocationAggregate] = {}
        self.snapshots: Dict[str, Dict] = {}

        print("系统初始化完成\n")

    def get_or_create_aggregate(self, sku: str) -> ProductAggregate:
        if sku not in self.aggregates:
            if sku in self.snapshots:
                events = self.event_store.get_events(sku, self.snapshots[sku]["version"])
                self.aggregates[sku] = ProductAggregate.restore_from_snapshot(self.snapshots[sku], events)
                print(f"  [快照恢复] 聚合根 {sku} 从快照恢复(版本 {self.snapshots[sku]['version']})")
            else:
                self.aggregates[sku] = ProductAggregate(sku)
        return self.aggregates[sku]

    def get_or_create_location(self, location_code: str) -> LocationAggregate:
        if location_code not in self.location_aggregates:
            self.location_aggregates[location_code] = LocationAggregate(location_code)
        return self.location_aggregates[location_code]

    def save_aggregate(self, aggregate: ProductAggregate):
        for event in aggregate.pending_events:
            self.event_store.append(event)
            if event.event_type in [EventType.STOCK_RECEIVED, EventType.STOCK_MOVED, EventType.STOCK_SHIPPED]:
                if "location" in event.payload:
                    loc = self.get_or_create_location(event.payload["location"])
                    loc.apply(event)
                if event.event_type == EventType.STOCK_MOVED:
                    from_loc = self.get_or_create_location(event.payload["from_location"])
                    to_loc = self.get_or_create_location(event.payload["to_location"])
                    from_loc.apply(event)
                    to_loc.apply(event)
        aggregate.pending_events.clear()

        if aggregate.version > 0 and aggregate.version % 10 == 0:
            self.snapshots[aggregate.sku] = aggregate.create_snapshot()
            print(f"  [快照创建] 聚合根 {aggregate.sku} 快照已创建(版本 {aggregate.version})")

    def receive_stock(self, sku: str, quantity: int, location: str, batch_no: str):
        print(f"\n[入库] SKU={sku}, 数量={quantity}, 库位={location}")
        aggregate = self.get_or_create_aggregate(sku)
        event = aggregate.receive_stock(quantity, location, batch_no)
        self.save_aggregate(aggregate)
        self.audit_logger.log_operation("入库", "操作员A", {"sku": sku, "qty": quantity})
        print(f"  [完成] {sku} 当前库存 {aggregate.total_quantity}")
        return event

    def move_stock(self, sku: str, quantity: int, from_location: str, to_location: str):
        print(f"\n[移库] SKU={sku}, 数量={quantity}, {from_location} -> {to_location}")
        lock_id = f"move_{sku}_{from_location}"
        identifier = str(uuid.uuid4())
        if not self.lock_manager.acquire(lock_id, identifier, timeout=5.0):
            raise Exception("获取分布式锁失败,移库操作被拒绝")
        try:
            aggregate = self.get_or_create_aggregate(sku)
            event = aggregate.move_stock(quantity, from_location, to_location)
            self.save_aggregate(aggregate)
            self.audit_logger.log_operation("移库", "叉车工B", {"sku": sku, "qty": quantity, "from": from_location, "to": to_location})
            print(f"  [完成] {sku} 从 {from_location} 移至 {to_location}")
            return event
        finally:
            self.lock_manager.release(lock_id, identifier)

    def allocate_stock(self, sku: str, quantity: int, order_id: str):
        print(f"\n[分配] SKU={sku}, 数量={quantity}, 订单={order_id}")
        aggregate = self.get_or_create_aggregate(sku)
        event = aggregate.allocate_stock(quantity, order_id)
        self.save_aggregate(aggregate)
        self.audit_logger.log_operation("分配", "系统", {"sku": sku, "qty": quantity, "order": order_id})
        print(f"  [完成] {sku} 已分配 {aggregate.allocated_quantity}")
        return event

    def ship_stock(self, sku: str, quantity: int, location: str, order_id: str):
        print(f"\n[出库] SKU={sku}, 数量={quantity}, 订单={order_id}")
        aggregate = self.get_or_create_aggregate(sku)
        event = aggregate.ship_stock(quantity, location, order_id)
        self.save_aggregate(aggregate)
        self.audit_logger.log_operation("出库", "发货员C", {"sku": sku, "qty": quantity, "order": order_id})
        print(f"  [完成] {sku} 剩余库存 {aggregate.total_quantity}")
        return event

    def adjust_stock(self, sku: str, location: str, physical_qty: int):
        print(f"\n[盘点] SKU={sku}, 库位={location}, 实盘={physical_qty}")
        aggregate = self.get_or_create_aggregate(sku)
        event = aggregate.adjust_stock(location, physical_qty)
        self.save_aggregate(aggregate)
        self.audit_logger.log_operation("盘点调整", "盘点员D", {"sku": sku, "location": location, "physical": physical_qty})
        print(f"  [完成] {sku} 差异 {event.payload['difference']}")
        return event

    def run_saga_inbound_allocate(self, sku: str, quantity: int, location: str, order_id: str):
        print(f"\n[Saga] 启动入库-分配流程: SKU={sku}, 数量={quantity}")
        saga_id = f"saga_{snowflake.next_id()}"
        aggregate = self.get_or_create_aggregate(sku)

        steps = [
            SagaStep(name="入库", action=lambda: self._saga_receive(sku, quantity, location, f"BATCH_{saga_id}"),
                     compensation=lambda: print("    [补偿] 取消入库记录")),
            SagaStep(name="分配", action=lambda: self._saga_allocate(sku, quantity, order_id),
                     compensation=lambda: print("    [补偿] 释放分配库存"))
        ]
        return self.saga_orchestrator.start_saga(saga_id, steps)

    def _saga_receive(self, sku, qty, loc, batch):
        aggregate = self.get_or_create_aggregate(sku)
        event = aggregate.receive_stock(qty, loc, batch)
        self.save_aggregate(aggregate)

    def _saga_allocate(self, sku, qty, order):
        aggregate = self.get_or_create_aggregate(sku)
        event = aggregate.allocate_stock(qty, order)
        self.save_aggregate(aggregate)

    def query_current_stock(self) -> Dict:
        print("\n[投影] 构建当前库存物化视图...")
        return self.projection_handler.build_current_stock_projection()

    def time_travel_query(self, sku: str, target_time: datetime) -> Dict:
        print(f"\n[TimeTravel] SKU={sku}, 时间点={target_time.isoformat()}")
        return self.time_travel.query_stock_at_time(sku, target_time)

    def replay_events(self, sku: str):
        print(f"\n[回放] 事件回放: SKU={sku}")
        events = self.event_store.get_events(sku)
        print(f"  共 {len(events)} 个事件,开始重放...")
        temp_aggregate = ProductAggregate(sku)
        for i, event in enumerate(events, 1):
            print(f"    [{i}/{len(events)}] {event.event_type.value} (v{event.aggregate_version}) - {event.timestamp.strftime('%H:%M:%S')}")
            temp_aggregate.apply(event)
        print(f"  [回放完成] 总库存={temp_aggregate.total_quantity}, 可用={temp_aggregate.available_quantity}, 已分配={temp_aggregate.allocated_quantity}")
        return temp_aggregate

    def generate_audit_report(self, hours: int = 24) -> Dict:
        print(f"\n[审计] 生成过去{hours}小时合规审计报告...")
        end_time = datetime.now()
        start_time = end_time - timedelta(hours=hours)
        return self.audit_logger.generate_compliance_report(start_time, end_time)

    def perform_physical_count(self, physical_counts: Dict[str, Dict[str, int]]) -> Dict:
        print("\n[对账] 执行物理盘点与系统数据差异分析...")
        return self.reconciliation.perform_reconciliation(physical_counts)

    def demonstrate_concurrency_control(self):
        print("\n[并发控制演示] 乐观并发控制(版本号冲突检测)...")
        sku = "SKU-TEST-CONCURRENT"
        aggregate = self.get_or_create_aggregate(sku)
        aggregate.receive_stock(100, "A01", "BATCH_INIT")
        self.save_aggregate(aggregate)

        # 模拟两个并发操作:从事件流重建两个独立的聚合根实例
        agg1 = ProductAggregate(sku)
        for ev in self.event_store.get_events(sku):
            agg1.apply(ev)

        agg2 = ProductAggregate(sku)
        for ev in self.event_store.get_events(sku):
            agg2.apply(ev)

        # 操作1:移库10个并保存
        event1 = agg1.move_stock(10, "A01", "A02")
        self.save_aggregate(agg1)

        # 操作2:分配20个(基于相同初始版本,应因版本冲突失败)
        try:
            event2 = agg2.allocate_stock(20, "ORDER_001")
            self.save_aggregate(agg2)
        except ConcurrentModificationException as e:
            print(f"  [检测成功] 并发冲突被正确检测: {e}")
            print("  [解决方案] 重试策略 - 重新加载聚合根后重试操作")

    def demonstrate_idempotency(self):
        print("\n[幂等性演示] 基于事件ID去重...")
        sku = "SKU-TEST-IDEMPOTENT"
        aggregate = self.get_or_create_aggregate(sku)
        event = aggregate.receive_stock(50, "B01", "BATCH_001")
        self.save_aggregate(aggregate)

        print(f"  尝试重复提交事件 ID={event.event_id}...")
        result = self.event_store.append(event)
        if not result:
            print("  [拦截成功] 幂等性保障:重复事件被拒绝")

    def print_event_store_stats(self):
        print("\n[统计] 事件存储统计信息:")
        all_events = self.event_store.get_all_events()
        print(f"  全局事件总数: {len(all_events)}")
        print(f"  聚合根数量: {len(self.aggregates)}")
        event_types = defaultdict(int)
        for e in all_events:
            event_types[e.event_type.value] += 1
        print("  事件类型分布:")
        for et, count in sorted(event_types.items()):
            print(f"    - {et}: {count}")
        delay_stats = self.projection_handler.get_projection_delay_stats()
        if delay_stats:
            print(f"  投影处理延迟: 平均={delay_stats['avg_ms']:.2f}ms, 最大={delay_stats['max_ms']:.2f}ms")

    def print_separator(self, title: str):
        print("\n" + "=" * 80)
        print(f"  {title}")
        print("=" * 80)


# ==============================================================================
# 主程序:完整仿真流程
# ==============================================================================
def main():
    wms = EventSourcingWMS()

    # 阶段一:基础业务操作
    wms.print_separator("阶段一:基础业务操作演示(10.1 领域建模)")
    wms.receive_stock("SKU-001", 1000, "A01-01", "BATCH-20260527-001")
    wms.receive_stock("SKU-001", 500, "A01-02", "BATCH-20260527-002")
    wms.receive_stock("SKU-002", 800, "B02-01", "BATCH-20260527-003")
    wms.move_stock("SKU-001", 200, "A01-01", "A01-03")
    wms.allocate_stock("SKU-001", 300, "ORDER-20260527-001")
    wms.allocate_stock("SKU-002", 150, "ORDER-20260527-002")
    wms.ship_stock("SKU-001", 100, "A01-01", "ORDER-20260527-001")

    # 阶段二:Saga事务
    wms.print_separator("阶段二:Saga跨聚合事务协调(10.4.4)")
    wms.run_saga_inbound_allocate("SKU-003", 500, "C03-01", "ORDER-SAGA-001")

    # 阶段三:并发控制与幂等性
    wms.print_separator("阶段三:并发控制与幂等性(10.4.1 / 10.4.2)")
    wms.demonstrate_concurrency_control()
    wms.demonstrate_idempotency()

    # 阶段四:投影与查询
    wms.print_separator("阶段四:投影与查询(10.3 投影与查询)")
    stock_view = wms.query_current_stock()
    print("\n[当前库存视图]")
    for sku, data in stock_view.items():
        print(f"  SKU: {sku}")
        print(f"    总库存: {data['total']}")
        print(f"    可用库存: {data['available']}")
        print(f"    已分配: {data['allocated']}")
        print(f"    库位分布: {dict(data['locations'])}")

    past_time = datetime.now() - timedelta(minutes=5)
    travel_result = wms.time_travel_query("SKU-001", past_time)
    print(f"\n[TimeTravel结果]")
    print(f"  查询时间: {travel_result['query_time']}")
    print(f"  基于事件数: {travel_result['based_on_events']}")
    print(f"  当时总库存: {travel_result['total_quantity']}")
    print(f"  当时可用库存: {travel_result['available_quantity']}")

    wms.replay_events("SKU-001")

    # 阶段五:盘点与差异分析
    wms.print_separator("阶段五:盘点与差异分析(10.5.3)")
    physical_counts = {
        "SKU-001": {"A01-01": 700, "A01-02": 500, "A01-03": 180},
        "SKU-002": {"B02-01": 850}
    }
    reconciliation = wms.perform_physical_count(physical_counts)
    print(f"\n[盘点对账结果]")
    print(f"  检查SKU数: {reconciliation['total_skus_checked']}")
    print(f"  匹配数: {reconciliation['matching_skus']}")
    print(f"  差异数: {reconciliation['discrepancy_count']}")
    print(f"  准确率: {reconciliation['accuracy_rate']:.1f}%")
    if reconciliation['discrepancies']:
        print(f"\n  差异明细:")
        for disc in reconciliation['discrepancies']:
            print(f"    [差异] {disc['sku']} @ {disc['location']}: 系统={disc['system_quantity']}, 实盘={disc['physical_quantity']}, 差异={disc['difference']} ({disc['status']})")

    # 阶段六:审计与合规
    wms.print_separator("阶段六:审计与合规(10.5.1 / 10.5.2)")
    audit_report = wms.generate_audit_report(hours=1)
    print(f"\n[合规审计报告]")
    print(f"  报告周期: {audit_report['report_period']}")
    print(f"  事件总数: {audit_report['total_events']}")
    print(f"  事件分布: {dict(audit_report['event_breakdown'])}")
    print(f"  操作员活动: {dict(audit_report['operator_activity'])}")
    print(f"  链完整性验证: {'通过' if audit_report['chain_integrity_verified'] else '失败'}")

    print(f"\n[区块链式事件链验证]")
    for sku in wms.aggregates.keys():
        valid = wms.event_store.verify_chain(sku)
        print(f"  {sku}: {'哈希链完整' if valid else '哈希链断裂'}")

    # 阶段七:性能统计
    wms.print_separator("阶段七:系统统计与总结(10.5.4)")
    wms.print_event_store_stats()

    print(f"\n[库位利用率]")
    for loc_code, loc_agg in wms.location_aggregates.items():
        util = loc_agg.get_utilization()
        items = dict(loc_agg.items)
        print(f"  {loc_code}: 利用率 {util:.1f}%, 存放商品: {items}")

    print(f"\n[序列化演示]")
    all_events = wms.event_store.get_all_events()
    if all_events:
        sample_event = all_events[0]
        serialized = EventSerializer.serialize(sample_event)
        print(f"  原始事件: {sample_event.event_type.value} (ID={sample_event.event_id})")
        print(f"  序列化大小: {len(serialized)} 字节")
        print(f"  Schema版本: {EventSerializer.SCHEMA_VERSION}")
        deserialized = EventSerializer.deserialize(serialized)
        print(f"  反序列化验证: {'成功' if deserialized.event_id == sample_event.event_id else '失败'}")

    print("\n" + "=" * 80)
    print("事件溯源仓储管理系统仿真完成!")
    print("=" * 80)


if __name__ == "__main__":
    main()

3. 运行说明

环境要求

  • Python 3.8+
  • 无需额外依赖(仅使用标准库)

运行步骤

bash 复制代码
# 1. 保存代码到文件
# 将上述代码保存为 wms_event_sourcing.py

# 2. 直接运行
python wms_event_sourcing.py

代码结构说明

复制代码
wms_event_sourcing.py
├── SnowflakeGenerator          # 10.2.3 全局排序ID
├── DomainEvent                 # 10.1.1 领域事件基类 + 10.5.1 哈希链
├── EventSerializer             # 10.2.2 Protobuf序列化模拟
├── EventStore                  # 10.2.1/10.2.4 事件存储与归档
├── ProductAggregate            # 10.1.2 商品聚合根 + 10.1.3 快照
├── LocationAggregate           # 10.1.2 库位聚合根
├── DistributedLock             # 10.4.3 Redis Redlock模拟
├── SagaOrchestrator            # 10.4.4 Saga事务协调
├── ProjectionHandler           # 10.3.1/10.3.2 投影处理器
├── TimeTravelQuery             # 10.3.3 时间旅行查询
├── AuditLogger                 # 10.5.2 审计日志
├── InventoryReconciliation     # 10.5.3 差异分析
└── EventSourcingWMS            # 主系统类(整合所有模块)

4. 输出示例

运行程序后,控制台将输出以下内容的中文可视化结果:

4.1 基础业务操作

复制代码
[入库] SKU=SKU-001, 数量=1000, 库位=A01-01
  [完成] SKU-001 当前库存 1000

4.2 Saga事务流程

复制代码
[启动Saga] saga_xxx
  [步骤 1/2] 入库
  [完成] 入库
  [步骤 2/2] 分配
  [完成] 分配
[Saga完成] saga_xxx

4.3 并发控制检测

复制代码
[并发控制演示] 乐观并发控制(版本号冲突检测)...
  [检测成功] 并发冲突被正确检测: 并发冲突:聚合根 SKU-TEST-CONCURRENT 期望版本 3,实际版本 2
  [解决方案] 重试策略 - 重新加载聚合根后重试操作

4.4 Time Travel查询

复制代码
[TimeTravel] SKU=SKU-001, 时间点=2026-05-27T21:25:00
[TimeTravel结果]
  查询时间: 2026-05-27T21:25:00
  基于事件数: 3
  当时总库存: 1500
  当时可用库存: 1500

4.5 事件回放调试

复制代码
[回放] 事件回放: SKU=SKU-001
  共 5 个事件,开始重放...
    [1/5] StockReceived (v1) - 21:30:15
    [2/5] StockReceived (v2) - 21:30:15
    [3/5] StockMoved (v3) - 21:30:15
    [4/5] StockAllocated (v4) - 21:30:15
    [5/5] StockShipped (v5) - 21:30:15
  [回放完成] 总库存=1400, 可用=1200, 已分配=200

4.6 盘点差异分析

复制代码
[对账] 执行物理盘点与系统数据差异分析...
[盘点对账结果]
  检查SKU数: 2
  匹配数: 2
  差异数: 2
  准确率: 100.0%

  差异明细:
    [差异] SKU-001 @ A01-03: 系统=200, 实盘=180, 差异=-20 (盘亏)
    [差异] SKU-002 @ B02-01: 系统=800, 实盘=850, 差异=50 (盘盈)

4.7 审计合规报告

复制代码
[审计] 生成过去1小时合规审计报告...
[合规审计报告]
  报告周期: 2026-05-27T20:30:00 至 2026-05-27T21:30:00
  事件总数: 12
  事件分布: {'StockReceived': 6, 'StockMoved': 2, 'StockAllocated': 3, 'StockShipped': 1}
  操作员活动: {'操作员A': 6, '叉车工B': 2, '系统': 3, '发货员C': 1}
  链完整性验证: 通过

4.8 区块链式哈希验证

复制代码
[区块链式事件链验证]
  SKU-001: 哈希链完整
  SKU-002: 哈希链完整
  SKU-003: 哈希链完整

5. 核心设计要点总结

章节 设计要点 实现方式
10.1.1 领域事件 DomainEvent 基类 + EventType 枚举
10.1.2 聚合根 ProductAggregate / LocationAggregate 状态重建
10.1.3 快照机制 每10版本自动快照 + restore_from_snapshot
10.1.4 版本号并发控制 aggregate_version 乐观锁检测
10.2.1 JSONB存储 内存 Dict 模拟 + defaultdict(list) 事件流
10.2.2 Protobuf序列化 EventSerializer JSON模拟 + Schema版本管理
10.2.3 Snowflake ID 41位时间戳 + 5位数据中心 + 5位工作节点 + 12位序列
10.2.4 归档策略 超过阈值50时自动归档50%事件到冷存储
10.3.1 投影处理器 ProjectionHandler 异步构建物化视图
10.3.2 当前库存查询 事件流折叠计算实时库存
10.3.3 Time Travel 过滤时间戳前的事件并重放状态
10.3.4 事件回放 replay_events 逐条打印并重建状态
10.4.1 乐观并发控制 版本号不匹配抛出 ConcurrentModificationException
10.4.2 幂等性保障 processed_ids 集合去重
10.4.3 分布式锁 DistributedLock 模拟Redlock协议
10.4.4 Saga事务 SagaOrchestrator 编排 + 补偿机制
10.5.1 不可篡改日志 SHA-256哈希链 + previous_hash 关联
10.5.2 操作审计 AuditLogger 全行为记录 + 合规报告
10.5.3 差异分析 InventoryReconciliation 物理vs系统比对
10.5.4 性能优化 投影延迟监控 + 批量归档

提示:本仿真系统使用内存存储,适用于教学演示。生产环境建议替换为:

  • 事件存储:PostgreSQL + JSONB 字段 + BRIN索引
  • 序列化:Google Protobuf / Apache Avro
  • 分布式锁:Redis Redlock / ZooKeeper
  • 消息队列:Kafka / RabbitMQ 用于投影异步处理
  • 快照存储:Redis / 独立数据库存储快照
相关推荐
charlee441 小时前
《GIS基础原理与技术实践》配套案例(Python版)
python·conda·numpy·gis·环境配置
繁华落尽,倾城殇?1 小时前
[C++11] : atomic,nullptr,default/delete,enum class
开发语言·c++·c++11·nullptr·atomic·enum class·default/delete
01_ice2 小时前
C语言数据在内存中的存储
c语言·开发语言
代码村新手2 小时前
C++-二叉搜索树
开发语言·c++
渣渣xiong3 小时前
从零开始:前端转型AI agent直到就业第五十七天-第五十八天
前端·人工智能·python
吃好睡好便好3 小时前
创建魔方矩阵和单位矩阵
开发语言·人工智能·学习·线性代数·matlab·矩阵
影寂ldy3 小时前
C#数组的属性和方法(Clear / Copy / IndexOf )
开发语言·javascript·c#
i7i8i9com3 小时前
Hermes Agent 安装记录
开发语言·bash·hermes
小娄~~4 小时前
C语言卷子错题集
c语言·开发语言·数据结构