用Function接口实现配置驱动型结算流程,核心是封装输入值加载器、查库逻辑和校验规则为可插拔函数,通过ensureValid方法统一执行校验与转换。
直接用 function 接口把结算逻辑从“复制粘贴式模板”变成“配置驱动型流程”,核心是剥离变化点、固化执行骨架。
识别结算中真正会变的三个要素
电商结算看似千差万别,但拆解后高频变化的只有三类:
- 输入值类型:订单ID、用户ID、优惠券码、SKU编码等——它们类型不同、来源不同,但都作为查询或计算的起点;
- 查库/取数逻辑:查订单主表、查用户余额、查库存快照、调第三方风控接口——每种数据源对应一个独立获取动作;
- 校验/转换规则:金额是否大于0、状态是否为“已支付”、是否在有效期内、是否满足满减门槛——这些判断决定后续能否继续或抛错。
用 Function 封装可插拔的数据获取器
不再为每个字段写一个 checkXxxExist() 方法。统一抽象为:
Function<String, Order> orderLoader = id -> orderDao.selectById(id); Function<String, UserAccount> accountLoader = userId -> accountService.getByUserId(userId); Function<String, Coupon> couponLoader = code -> couponService.getByCode(code);
然后放进一个策略容器里:
Map<String, Function<String, ?>> loaders = new HashMap<>();
loaders.put("ORDER_ID", orderLoader);
loaders.put("USER_ID", accountLoader);
loaders.put("COUPON_CODE", couponLoader);
结算时按需取用,传入原始值,拿到结构化对象,后续逻辑完全复用。
用 BiFunction + Predicate 组合封装校验断言
把“查出来之后要怎么判”也函数化。例如:
- 判断订单是否可结算:
Predicate<Order> canSettle = o -> "PAID".equals(o.getStatus()) && o.getPayTime().after(earliestTime); - 判断用户余额是否充足:
BiFunction<UserAccount, BigDecimal, Boolean> hasEnough = (acc, amount) -> acc.getBalance().compareTo(amount) >= 0;
这些函数可预先注册进规则中心,结算引擎通过 key 动态加载,无需改代码就能上线新校验项。
组装成可复用的通用结算断言方法
最终落地一个高内聚的方法,比如:
public static <T> T ensureValid(
String rawValue,
Function<String, T> loader,
Predicate<T> validator,
String errorMsg
) {
T obj = loader.apply(rawValue);
if (obj == null || !validator.test(obj)) {
throw new SettlementException(errorMsg);
}
return obj;
}
调用时清爽直接:
Order order = ensureValid(
orderId,
orderLoader,
canSettle,
"订单不存在或不可结算"
);
UserAccount account = ensureValid(
userId,
accountLoader,
acc -> acc.getStatus() == 1,
"用户账户异常,无法扣款"
);
新增一种结算场景?只需补两行函数定义,不碰主干逻辑。

















