2. 账户状态变更与生命周期管理

任务目标

在账户对象上实现状态机:正常(ACTIVE)、冻结(FROZEN)、已销户(CLOSED)。支持冻结、解冻、销户三个操作,每次变更写入状态变更日志(含时间、原状态、新状态、原因)。非法变更(如已销户再冻结、冻结中销户)必须被拒绝。

状态机规则

  • ACTIVE → FROZEN(冻结)、ACTIVE → CLOSED(销户)
  • FROZEN → ACTIVE(解冻)、FROZEN → CLOSED(销户)
  • CLOSED 为终态,不允许任何变更
ACTIVE 正常 FROZEN 冻结 CLOSED 销户 冻结 解冻 销户 销户

完整实现(Python)

from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum

class Status(Enum):
    ACTIVE = "ACTIVE"
    FROZEN = "FROZEN"
    CLOSED = "CLOSED"

# 合法状态迁移表:{原状态: {允许的新状态}}
TRANSITIONS = {
    Status.ACTIVE: {Status.FROZEN, Status.CLOSED},
    Status.FROZEN: {Status.ACTIVE, Status.CLOSED},
    Status.CLOSED: set(),
}

@dataclass
class ChangeLog:
    time: str
    from_status: Status
    to_status: Status
    reason: str

@dataclass
class Account:
    account_id: str
    status: Status = Status.ACTIVE
    logs: list = field(default_factory=list)

    def _change(self, target: Status, reason: str) -> None:
        if target not in TRANSITIONS[self.status]:
            raise ValueError(
                f"非法状态变更: {self.status.value} -> {target.value}"
            )
        self.logs.append(ChangeLog(
            time=datetime.now().isoformat(timespec="seconds"),
            from_status=self.status,
            to_status=target,
            reason=reason,
        ))
        self.status = target

    def freeze(self, reason: str) -> None:
        self._change(Status.FROZEN, reason)

    def unfreeze(self, reason: str) -> None:
        self._change(Status.ACTIVE, reason)

    def close(self, reason: str) -> None:
        self._change(Status.CLOSED, reason)

if __name__ == "__main__":
    acc = Account("A1001")
    acc.freeze("司法冻结")
    acc.unfreeze("冻结解除")
    acc.close("客户销户")

    for log in acc.logs:
        print(f"{log.time} {log.from_status.value}->{log.to_status.value} ({log.reason})")
    print("最终状态:", acc.status.value)

    # 验证非法变更被拒绝
    try:
        acc.freeze("再次冻结")
    except ValueError as e:
        print("已拒绝:", e)

运行与验证

环境:Python 3.10+,无第三方依赖。保存为 account.py 后执行 python account.py。

通过条件:

  • 打印出 3 条日志,依次为 ACTIVE→FROZEN、FROZEN→ACTIVE、ACTIVE→CLOSED,且每条含时间与原因;
  • 最终状态为 CLOSED;
  • 对已销户账户再次冻结时抛出 ValueError,被 except 捕获并打印“已拒绝: 非法状态变更: CLOSED -> FROZEN”。

扩展验证:可对 FROZEN 状态直接调用 close(),应成功;对 CLOSED 调用 unfreeze() 应被拒绝。