面向前端的产品匹配规则说明

一句话结论

前端最终看到的产品,不是 H5 自己根据 cust_level 推导出来的,也不是代码按更短 Span 自动向下兼容出来的,而是:

  1. 风控返回客户等级 cust_level
  2. PFS/BNS/前端服务用该等级查询 pfs_cust_level_product
  3. 一个 cust_level 可以配置多个 product_fee_id,用来覆盖该等级可用的多个跨度、多个期数。
  4. 服务侧再按产品状态、MaxTermspen = span_days * term 过滤和排序。
  5. 正常等级查不到可用产品时,才进入 DD 兜底。

H5 / App 只消费服务返回的产品列表,负责展示、默认选中、试算和提交。

核心概念

cust_level 命名格式来自飞书「命名规则」:

{Segment}_{Span}_{MaxTerm}

例如 A_7D_4

字段含义示例
Segment客群风险等级,A-Z 风险依次升高;DD 是兜底等级A
Span产品跨度/计息周期7D
MaxTerm该等级最大可用期数4

当前 CashNaija 产品池:

产品product_idtermspan_daysspen
7D x 1T20000177
7D x 2T200522714
7D x 3T200553721
7D x 4T200634728

spen = span_days * term,用于过滤和排序。当前只有 7D,所以排序效果等价于按 term 倒序。

配置口径

pfs_cust_level_product 是产品匹配的配置入口。

关键规则:

  • 一个 cust_level 可以配置多个 product_fee_id
  • 如果一个等级需要覆盖多个期数,就给同一个等级配置多个期数对应的 product_fee_id
  • 如果后续启用 14D / 28D / 30D 等更多跨度,也是在同一个等级下配置多个不同跨度的 product_fee_id

A_7D_4 为例,如果希望它可用 4 个期数,应在 pfs_cust_level_product 里直接配置:

cust_levelproduct_fee_idproduct_idterm产品
A_7D_4400142006347D x 4T
A_7D_4400132005537D x 3T
A_7D_4400122005227D x 2T
A_7D_4400112000017D x 1T

排序后仍是:

40014 -> 40013 -> 40012 -> 40011

主流程

flowchart TD
    A["风控返回 cust_level<br/>例如 A_7D_4"] --> B{"cust_level 是否有效"}
    B -- "否" --> K["切换到 DD 兜底等级"]
    B -- "是" --> D["查 pfs_cust_level_product<br/>cust_level = A_7D_4"]
    D --> E["得到已配置的多个 product_fee_id"]
    E --> F["关联 pfs_product_fee / pfs_product / pfs_acct_type"]
    F --> G["计算 term / span_days / spen"]
    G --> H["按状态、MaxTerm、最大 spen 过滤"]
    H --> I{"是否有可用产品"}
    I -- "有" --> J["按 spen 倒序排序"]
    I -- "无" --> K["切换到 DD 兜底等级"]
    K --> L["查 DD 等级配置"]
    L --> M["读取 DD 等级下配置的 product_fee_id"]
    M --> P["关联产品并执行相同过滤排序"]
    P --> Q{"DD 是否有可用产品"}
    Q -- "有" --> J
    Q -- "无" --> R["返回空产品或业务兜底异常"]
    J --> N["BNS 补充额度、去重、默认产品"]
    N --> O["H5 / App 展示、试算、提交"]

过滤和排序

服务侧拿到配置候选后,再做过滤和排序。

过滤条件:

  1. 产品状态可用。
  2. term <= cust_level.max_term
  3. spen <= cust_level.span_days * cust_level.max_term

排序规则:

  1. spen 倒序。
  2. 如果 spen 相同,span_days 更大的在前。
  3. 如果 span_days 也相同,term 更大的在前。

如果业务要求最多展示 3 个产品,应在统一承接侧截断前 3 个。

MaxTerm 取数逻辑(产品流程跑通后再迭代)

A_7D_4 为例:

  1. 解析等级:
Segment = A
Span = 7D
MaxTerm = 4
max_spen = 7 * 4 = 28
  1. 查询配置:
SELECT product_fee_id
FROM pfs_cust_level_product
WHERE cust_level = 'A_7D_4'
  AND status = 1;
  1. 假设配置返回:
40014, 40013, 40012, 40011
  1. 关联产品后过滤和排序:
cust_levelproduct_fee_idproduct_idtermspan_daysspen是否保留
A_7D_440014200634728保留
A_7D_440013200553721保留
A_7D_440012200522714保留
A_7D_44001120000177保留

如果配置里误配了超过 MaxTerm 或超过最大 spen 的产品,服务侧会过滤掉。

DD 兜底

DD 是兜底客群等级,只在以下情况触发:

  • 风控没有返回 cust_level
  • cust_level 格式不符合 {Segment}_{Span}_{MaxTerm}
  • 正常等级查不到可用产品。

触发兜底时,用户兜底产品等级统一使用 DD_7D_2

原始场景兜底等级
风险等级为空或无法解析DD_7D_2
A_7D_4 查不到可用产品DD_7D_2
B_7D_2 查不到可用产品DD_7D_2
Z_7D_1 查不到可用产品DD_7D_2

DD 等级同样走配置多映射,不靠代码生成多个 DD 等级。

例如默认兜底等级 DD_7D_2 可配置:

cust_levelproduct_fee_idproduct_idterm产品
DD_7D_2400022005227D x 2T
DD_7D_2400012000017D x 1T

注意:正常等级能查到可用产品时,不混入 DD 产品,否则会让客户等级定价失效。

前后端职责

层级职责
风控 / Nonfinancial返回客户风险等级 riskLevel
BNS获取风险等级,调用 PFS 查询产品;补充额度、去重、默认产品
PFScust_level 查询 pfs_cust_level_product,组装产品、费率、费项、账期并排序
H5 / App消费返回产品列表,展示、默认选中、试算、提交

当前代码链路(供参考):

  1. H5 普通提现页调用 /bns/query/queryCustProductInfo
  2. 还款复借 / 预约再借页调用 /bns/query/queryCustRepayInfo
  3. BNS 查询风险等级,并把 riskLevel 写入 QuerySimpleProductReq.custLevel
  4. BNS 调用 PFS queryProductList
  5. PFS 查询 pfs_cust_level_product 中该等级已配置的 product_fee_id
  6. PFS 组装产品详情并按 loan_span_count * term 排序。
  7. BNS 根据客户额度、lmtRateminAmt/maxAmt 等二次处理。
  8. H5 使用 querySimpleProductResp.productList 展示和试算。

实现注意事项

  • pfs_cust_level_product 是产品可见性的配置源;不要仅凭 cust_level 文本生成不存在于配置表里的产品。
  • 同一个 cust_level 可以有多条 product_fee_id 映射,这是覆盖多期数、多跨度的核心方式。
  • 后续启用更多跨度时,仍通过配置多条映射覆盖,不通过代码自动向下兼容。
  • DD 只在正常等级无可用产品时触发。

关联功能改造

FLAT 计息支持保险、税费费用

2T-4T 产品配置了保险 2-3%(见 05_fee_table.sql),但当前 ScheduleCalcForFlat.java 写死「不含保险和手续费」,排期只生成 PRINCIPAL + INT 两种余额成分。NonfinancialBusiness.java 调用 FLAT 时也写死传 0。

对比 EPI 计算器(ScheduleCalcForEPI.java 第 125-133 行),EPI 是配置驱动的:insuranceAmt > 0 就加 INSURANCE_FEE 成分,handleAmt > 0 就加 HANDLING_FEE 成分。后续新增任何费项,只要 pfs_fee_table 配置了,排期计算器应自动包含。

改造内容:

  1. ScheduleCalcForFlat.java 在排期生成循环中,增加保险和手续费余额成分逻辑,参照 EPI 第 125-133 行。
  2. NonfinancialBusiness.java 去掉 Flat 产品始终传 0 的写死逻辑,改为从 pfs_fee_table 读取费率并计算保险、手续费金额。

改造后,FLAT 排期生成时应自动根据 pfs_fee_table 配置决定是否包含 INSURANCE_FEE、HANDLING_FEE 等费用。后续新增费项时,优先通过配表生效。