拦截非法金额需用不可变快照结构封装amount_raw、account_id、currency、validation_rules,并绑定至自定义异常类,通过上下文保持器统一采集、全局处理器落库联动。

在交易逻辑中拦截非法金额,关键不是“拦住就完事”,而是让拦截动作自带上下文、可追溯、能联动。数据结构和自定义异常类要协同工作:前者负责轻量、结构化地承载业务现场信息,后者负责语义明确地表达问题本质,并把数据结构“焊”进异常对象里。
设计最小必要快照数据结构
非法金额的判定往往依赖多个关联字段,不能只存一个数字。推荐用不可变字典(如 Python 的 frozen dict 或 Java 的 ImmutableMap)封装四类最小字段:
- amount_raw:原始输入值(含类型,如字符串"100.0000"或 BigDecimal)
- account_id:操作账户,用于后续余额核验与风控关联
- currency:币种代码(如 "USD"),避免精度误判
- validation_rules:触发的校验规则标识(如 ["positive", "max_10m", "multiple_of_0.01"])
这个结构不查库、不序列化大对象,仅从当前方法参数或线程上下文提取,毫秒级完成。
定义带快照绑定能力的异常类
异常类不是错误提示的容器,而是业务状态的快照载体。所有金额相关异常应继承统一基类:
- Java 示例:InvalidAmountException extends BusinessException,构造时强制传入上述快照 map,并重写 getSnapshot() 方法直接返回
- Python 示例:class InvalidAmountError(Exception),在 __init__ 中接收 snapshot: dict,并提供 to_dict() 方法输出含时间戳的标准化结构
- 禁止使用 RuntimeException 或 Exception 直接抛出——那等于放弃上下文
在验证入口处统一注入快照
不要等 if 判断失败再拼数据。应在进入金额校验前,就用轻量上下文保持器采集快照:
- 在 service 方法开头,调用 AmountContext.capture(accountId, rawInput, currency)
- 该方法自动识别输入格式、提取币种、记录当前毫秒时间戳,生成快照结构
- 后续任意位置抛出 InvalidAmountException 时,直接复用该快照,无需重复取值
这样即使校验逻辑嵌套多层(如先转格式、再比限额、最后验精度),快照始终是同一份“事发当时”的快照。
让拦截结果可消费、可联动
抛出异常只是起点。快照必须落地为可用信息:
- 全局异常处理器捕获后,将 snapshot 异步写入专用表,字段包括 order_id(若已生成)、error_code、snapshot_json、created_at
- 日志框架(如 Log4j2 MDC 或 Structlog)配置自动提取 e.snapshot,每条日志天然带结构化金额上下文
- 风控系统监听该表,若发现同一 account_id 在 1 分钟内连续触发 3 次 InvalidAmountError 且 amount_raw 趋势异常(如逐次翻倍),自动标记为试探性攻击并限流
不复杂但容易忽略

















