面向对象设计要求异常成为业务状态的自然延伸,通过AmountInput封装输入现场、专用异常子类实体化错误、策略链校验及全局处理器精准映射响应。

面向对象设计不是给异常套个类名,而是让异常成为业务状态的自然延伸。拦截非法金额的关键,在于把“谁、在什么上下文中、因哪条规则失败”这些信息,用对象的方式固化下来,而不是靠字符串拼接或临时 map 传递。
用领域对象封装金额输入现场
不要让校验逻辑直接操作原始字符串或 double。定义一个不可变的 AmountInput 类,它包含:
• amount_raw:保留原始输入(如 "100.000" 或 "¥250"),不提前 parse
• account_id:触发操作的账户标识
• currency:明确币种,避免 USD 和 JPY 混用精度逻辑
• timestamp:毫秒级时间戳,用于后续审计对齐
这个对象在 Controller 或网关入口就构建完成,后续所有校验步骤都基于它展开,确保“输入现场”不被污染或丢失。
异常类即业务错误的实体化表达
每个非法金额场景对应一个具体子类,且必须继承统一基类:
• InvalidAmountException:基类,含 errorCode、context map、cause
• NegativeAmountException:专用于负数,构造时自动设 errorCode = AMT_NEGATIVE
• PrecisionMismatchException:携带 currency 和 expectedScale 字段,比如 JPY 要求 scale=0
• OverflowAmountException:含 maxAllowed 值和 rawInput,便于前端展示“不能超过 XX”
所有子类在构造时强制传入 AmountInput 实例,并通过 getOrigin() 方法暴露它——异常本身就能回答“当时发生了什么”。
校验流程遵循命令式对象协作
把金额校验拆成可组合的策略对象,每个策略接收 AmountInput 并返回 ValidationResult:
• PositiveRule.validate(input) → 失败则 new NegativeAmountException(input)
• PrecisionRule.validate(input) → 失败则 new PrecisionMismatchException(input)
• LimitRule.validate(input) → 失败则 new OverflowAmountException(input, limit)
Service 层不再写 if-else 堆砌,而是顺序调用策略链。任一策略抛出异常,整个流程中断,但异常已自带完整上下文,无需额外补录。
全局处理器实现语义到响应的精准映射
Spring 的 @ControllerAdvice 统一捕获 BusinessException 子类:
• 根据异常类型决定 HTTP 状态码(如 NegativeAmountException → 400,HighRiskAmountException → 429)
• 响应体固定结构:{ "code": "AMT_NEGATIVE", "message": "金额不能为负", "trace_id": "...", "input": { ... } }
• 同时异步写入 audit_amount_error 表,字段包括 account_id、currency、error_code、input_json、created_at
这样前端能按 code 做差异化提示,风控系统能按 error_code 订阅告警,审计人员能直接查原始输入快照。

















