Files
gaokao-volunteer-system/data/orders
Hermes Agent e18abedb4f feat(orders): T11.2 敏感字段展示脱敏工具 + to_dict 三态策略
- 新增 data/orders/masking.py (141 行):
  - mask_phone: 11 位 → 138****1234;支持 +86/86 国家码剥离、空格/横杠分隔符
  - mask_id_card: 18 位 → 430102********1234(保留前 6 行政区划 + 末 4);15 位老版兼容;13-14 位非标准也走前 6 后 4 遮罩
  - mask_name: 1 字原样 / 2 字 张* / 3 字 张*丰 / 4+ 字 张**;非中日韩字符全遮
  - mask_sensitive_dict: 一键遮罩订单字典中所有已知 PII 字段,不动 _enc/_hash 索引/密文
  - 默认安全:None/空串/非字符串均不抛错,统一返回 None/''

- 扩展 Order.to_dict(decrypt_sensitive) 三态策略:
  - True   : 完整明文(权限内接口使用,如后台人工核对)
  - False  : 完全移除明文(对外公开统计/审计日志)
  - 'mask' : 部分遮罩(默认,推荐;列表/详情直接可用)
  - 未知字符串策略值回退为 mask(防误传 plaintext 导致明文泄露)

- 新增 data/orders/tests/test_masking.py (246 行,32 个 pytest 用例):
  - 覆盖各 mask 函数边界(标准/带 +86/短串/非法输入)
  - 覆盖 Order.to_dict 三态与默认 mask 的串联通路

- 验证:
  - data/orders/tests 全套 112 用例通过(原 80 + 新增 32)
  - 全仓 344/344 通过
  - ruff check 0 warning / ruff format --check 已规范

- 与 T4.1 落盘加密的关系:crypto 负责'落盘形态'(密文+hash),
masking 负责'展示形态'(部分遮罩),两者正交互补。
2026-06-12 16:26:57 +08:00
..

订单数据模块 (T4.1)

提供订单的 SQLite schema、AES-256 加密Fernet、6 态状态机、Order dataclass。

目录结构

data/orders/
├── __init__.py
├── README.md
├── crypto.py            # Fernet 加密/解密/哈希派生
├── masking.py           # 展示脱敏工具 (T11.2)
├── state_machine.py     # 6 态转换验证
├── models.py            # Order dataclass + 序列化(to_dict 支持 mask 策略)
├── schema.py            # DDL + apply_schema()
└── tests/
    ├── __init__.py
    ├── test_crypto.py
    ├── test_masking.py
    ├── test_state_machine.py
    └── test_schema.py

字段分类

类别 处理方式 字段
强敏感 PII AES-256 加密 + 展示遮罩 customer_phone, candidate_id_card
弱敏感 PII 脱敏存储 + 展示遮罩 customer_name, candidate_name
索引用哈希 SHA-256 hex customer_phone_hash
业务字段 明文 amount_cents, source, status
元数据 明文 created_at, tags

T11.2 起,Order.to_dict() 默认走"展示遮罩"(如 138****1234 API 响应 / 前端列表无需再做额外处理。

6 态状态机

pending → paid → serving → delivered → completed
   │        │        │          │
   └──┬─────┴────────┴──────────┘
      ▼
  refunded (终态)

completed / refunded 为终态,不可再转换。

加密密钥

环境变量 GAOKAO_ORDERS_FERNET_KEY 必须配置;缺失时 get_fernet()MissingEncryptionKey禁止降级为明文

生产环境密钥生成示例:

python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"

快速使用

import os
os.environ["GAOKAO_ORDERS_FERNET_KEY"] = "your-secret-here"

from data.orders.schema import apply_schema
from data.orders.crypto import encrypt, decrypt, hash_for_index
from data.orders.state_machine import OrderStatus, assert_valid_transition
from data.orders.models import Order, generate_order_id

# 1. 建表
conn = apply_schema("data/orders.db")

# 2. 创建订单
order = Order(
    id=generate_order_id(),
    source="xianyu",
    service_version="standard",
    amount_cents=9900,
    status=OrderStatus.PENDING.value,
    customer_name="张*",
    customer_phone="13800001234",
    candidate_name="张同学",
)
conn.execute(
    "INSERT INTO orders (...) VALUES (...)",
    order.to_db_row(),
)

# 3. 状态转换
assert_valid_transition(order.status, "paid")

验收DoD

  • AES-256 加密Fernet对 customer_phone / candidate_id_card 落盘
  • 6 态状态机 + 转换合法性校验
  • SQLite schema 幂等可重建
  • 外键约束启用
  • SHA-256 hash 字段支持手机号去重
  • CHECK 约束拒绝非法 status
  • 展示脱敏T11.2Order.to_dict 默认走 mask 策略(手机/身份证/姓名)

展示脱敏 (T11.2)

为防止 API 响应 / 前端 / 日志 / 截图泄露完整 PII订单数据模型支持"展示遮罩"

from data.orders.models import Order

order = Order(
    id="GKO-20260612-AAAA",
    source="web",
    service_version="basic",
    customer_name="张三",
    customer_phone="13800001234",
    candidate_id_card="430102200501011234",
)

# 默认:mask 模式 — 适合 API 响应 / 前端
order.to_dict()
# {
#   "customer_phone": "138****1234",
#   "candidate_id_card": "430102********1234",
#   "customer_name": "张*",
#   ...
# }

# 显式明文(后台人工核对场景)
order.to_dict(decrypt_sensitive=True)

# 完全移除(对外公开统计/审计日志)
order.to_dict(decrypt_sensitive=False)

masking 模块是纯字符串工具,与 crypto 正交可在任意层API 序列化、Jinja2 模板过滤器、日志格式化、CSV 导出器)独立复用。

下游衔接

  • T4.2 DAO: 调用 apply_schema() 建表,使用 state_machine.assert_valid_transition() 守护状态变更
  • T4.3 CLI: 直接 import Order dataclass + crypto
  • T6.1 FastAPI: schema 不变DAO 复用

版本

v1.1 — 2026-06-12 — T11.2 展示脱敏(mask 策略 + masking.py) v1.0 — 2026-06-12 — T4.1 实施