PHP项目风控规则引擎设计

wen PHP项目 2

本文目录导读:

PHP项目风控规则引擎设计

  1. 核心概念与目标
  2. 整体架构设计(分层清晰)
  3. 核心数据模型设计
  4. 规则执行流程(核心逻辑)
  5. 关键技术实现细节
  6. 进阶与扩展
  7. 一个简单的 operator 内部实现示例

这是一个关于PHP项目风控规则引擎设计的专业问题,一个好的规则引擎是风控系统的核心,它决定了系统的灵活性、性能和可维护性。

下面我将从核心概念、架构设计、数据结构、规则执行流程、关键实现细节和进阶方案这几个方面,为你提供一个全面的设计思路。

核心概念与目标

  1. 规则 (Rule): 一个具体的判断逻辑,年龄 > 18 AND 登录IP属于黑名单,由条件动作组成。
  2. 条件 (Condition): 对输入数据(事件)的原子性判断,amount > 1000
  3. 动作 (Action): 当规则被触发时执行的操作,拒绝交易标记为高风险发送短信验证
  4. 事件 (Event): 需要被风控系统评估的原始数据,用户注册事件支付请求事件
  5. 策略 (Policy) / 规则集 (Rule Set): 一组规则的集合,高风险交易拦截策略,通常有优先级和组合逻辑(AND/OR)。
  6. 风控结果 (Result): 引擎执行完毕后输出的结论,通常包含 通过拒绝人工审核 等,以及命中的规则详情。

设计目标

  • 灵活性:业务人员(非开发)能通过后台配置规则,快速应对新的欺诈场景。
  • 高性能:每个请求的处理时间应尽可能短(毫秒级),避免成为性能瓶颈。
  • 可扩展性:易于添加新的算子(判断逻辑)、数据源、动作类型。
  • 可观测性:能记录每次评估的详细过程,便于排查和调优。

整体架构设计(分层清晰)

一个典型的PHP风控引擎架构可以分为四层:

[触发层] ---> [接入层/API] ---> [决策层/引擎] ---> [存储层/数据服务]
                                       |
                                       +---> [执行层/规则计算]
  • 触发层: 各种业务系统(订单、用户、支付)通过API调用发起风控请求。
  • 接入层/API: 统一认证、参数校验、将业务数据转换为引擎能理解的标准事件
  • 决策层/引擎核心
    • 特征计算: 从事件数据或外部数据源(数据库、缓存、第三方API)计算需要的特征值。
    • 规则匹配: 加载匹配的策略和规则,进行条件计算。
    • 决策输出: 聚合所有规则的匹配结果,生成最终决策(通过/拒绝/人工)。
  • 存储层/数据服务
    • 配置: 存储规则、名单、策略配置(如MySQL + Redis)。
    • 动态数据: 存储用户历史行为、设备指纹、实时计数器(如Redis)。

核心数据模型设计

规则配置 (存储在 MySQL/PostgreSQL 中)

-- 规则基础表
CREATE TABLE `rule` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `rule_code` varchar(64) NOT NULL COMMENT '规则编码,唯一',
    `name` varchar(128) NOT NULL COMMENT '规则名称',
    `priority` int(11) NOT NULL DEFAULT 0 COMMENT '优先级,数字越小越优先',
    `status` tinyint(1) NOT NULL DEFAULT 1 COMMENT '状态:1启用 0禁用',
    `action` varchar(32) NOT NULL COMMENT '命中后的动作: REJECT, REVIEW, ALLOW',
    `action_value` varchar(255) DEFAULT NULL COMMENT '动作参数(如拒绝原因码、是否需要验证码等)',
    `created_at` datetime NOT NULL,
    `updated_at` datetime NOT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_rule_code` (`rule_code`),
    KEY `idx_priority` (`priority`),
    KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 规则条件表 (支持AND/OR组合)
CREATE TABLE `rule_condition` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `rule_id` int(11) NOT NULL COMMENT '关联规则ID',
    `group_id` int(11) NOT NULL DEFAULT 0 COMMENT '条件组ID,同一组内用AND连接,不同组间用OR连接',
    `operator` varchar(32) NOT NULL COMMENT '算子: eq, neq, gt, gte, lt, lte, in, not_in, contains, starts_with, match_regex, etc.',
    `field` varchar(128) NOT NULL COMMENT '特征字段名(如 ip, amount, user_id)',
    `value` text NOT NULL COMMENT '比对值(如 192.168.1.1, 1000, [1,2,3])',
    `value_type` varchar(16) NOT NULL DEFAULT 'string' COMMENT '值类型: string, number, list, regex',
    `created_at` datetime NOT NULL,
    PRIMARY KEY (`id`),
    KEY `idx_rule_id` (`rule_id`),
    KEY `idx_group_id` (`rule_id`, `group_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

特征与事件传递(内存对象)

// 1. 标准事件对象 (由触发层传入)
class Event {
    // 事件类型: user.register, order.pay, loan.apply
    public string $eventType;
    // 事件携带的原始数据
    public array $rawData = [];
    // ... getter/setter
}
// 2. 特征上下文 (引擎内部使用)
class FeatureContext {
    // 已经计算的特征值
    private array $features = [];
    // 获取特征(支持懒加载)
    public function get(string $name): mixed {
        if (!array_key_exists($name, $this->features)) {
            // 如果特征未计算,调用FeatureProvider
            $this->features[$name] = FeatureProvider::calculate($name, $this->event);
        }
        return $this->features[$name];
    }
}

规则执行流程(核心逻辑)

以一个支付风控为例,伪代码实现如下:

<?php
class RiskEngine {
    private RuleLoader $ruleLoader;
    private FeatureCalculator $featureCalculator;
    private ActionResult $result;
    public function evaluate(Event $event): ActionResult {
        $this->result = new ActionResult();
        $context = new EvaluationContext($event);
        // 1. 加载所有启用的规则
        $rules = $this->ruleLoader->loadEnabledRules();
        // 2. 如果没有规则,默认通过
        if (empty($rules)) {
            $this->result->setDecision(Decision::ALLOW);
            return $this->result;
        }
        // 3. 按优先级排序
        usort($rules, function($a, $b) {
            return $a->priority <=> $b->priority;
        });
        // 4. 逐规则匹配
        foreach ($rules as $rule) {
            // 4.1 检查规则是否需要此事件类型(可选,通常是按策略匹配)
            // if (!in_array($event->eventType, $rule->getEventTypes())) continue;
            // 4.2 执行规则条件判断
            if ($this->matchRule($rule, $context)) {
                // 4.3 命中规则,记录命中详情
                $this->result->addHitRule($rule);
                // 4.4 执行规则动作(如果动作是REJECT,直接短路,不再继续)
                if ($rule->action === 'REJECT') {
                    $this->result->setDecision(Decision::REJECT);
                    $this->result->setRejectReason($rule->action_value);
                    // 记录日志
                    return $this->result;
                } elseif ($rule->action === 'REVIEW') {
                    // 如果需人工审核,继续评估其他规则,但最终结果标记为REVIEW
                    $this->result->setDecision(Decision::REVIEW);
                }
            }
        }
        // 5. 如果所有规则都未触发REJECT,返回最终决策(可能是ALLOW或REVIEW)
        if (!$this->result->getDecision()) {
            $this->result->setDecision(Decision::ALLOW);
        }
        return $this->result;
    }
    private function matchRule(Rule $rule, EvaluationContext $context): bool {
        // 规则内的条件是 AND/OR 组合关系
        // 假设条件按group_id分组,同一组内AND,不同组间OR
        $groupsResult = [];
        foreach ($rule->getConditionGroups() as $groupId => $conditions) {
            $groupResult = true;
            foreach ($conditions as $condition) {
                // 获取特征值(延迟计算)
                $featureValue = $context->getFeature($condition->getField());
                // 执行条件比对
                if (!$this->evaluateCondition($condition, $featureValue)) {
                    $groupResult = false;
                    // 一个组内AND,有一个为false则整个组为false
                    break;
                }
            }
            $groupsResult[] = $groupResult;
        }
        // 不同组之间是OR关系
        return in_array(true, $groupsResult);
    }
    private function evaluateCondition(Condition $cond, $featureValue): bool {
        // 这里实现具体的算子逻辑
        return OperatorFactory::create($cond->getOperator())->evaluate($featureValue, $cond->getValue());
    }
}
// 输出结果
class ActionResult {
    private string $decision; // ALLOW, REJECT, REVIEW
    private array $hitRules = [];
    private string $rejectReason = '';
    // ... getter/setter
}

关键技术实现细节

  1. 算子设计 (Operator Pattern)

    • 定义 OperatorInterface,包含 evaluate($featureValue, $ruleValue): bool 方法。
    • 实现 EqualOperatorGreaterThanOperatorInListOperatorRegexMatchOperator 等。
    • 好处:易于扩展,新增一个算子只需写一个新类并注册到 OperatorFactory
  2. 特征计算 (Feature Provider)

    • 设计中不要直接写死特征计算逻辑。
    • 采用延迟加载,只在特征被规则需要时才计算。
    • 使用缓存,同一个事件中相同特征只计算一次。
    • 示例:
      • user_total_deposit:从数据库或缓存中读取。
      • ip_risk_score:调用第三方API。
      • device_fingerprint:从事件中提取。
    • 可以做成插件化、可配置的。
  3. 名单/黑白名单处理

    • 将名单库(黑名单手机号、IP等)存储在Redis SetBloom Filter 中,实现O(1)时间复杂度判定。
    • 通常在特征计算阶段,计算出 is_uin_blacklisted 这样的特征,然后规则直接使用。
  4. 软规则 vs 硬规则

    • 硬规则:拒绝类,命中即返回。
    • 软规则:打分、标记,累积分数或标签,最后根据总分决策。
    • 设计时考虑两者结合:
    // 在规则中添加一个 score 字段
    $rule->score = 20; // 每条规则配置分值
    // 引擎评估
    $this->result->addHitRule($rule);
    $this->result->addScore($rule->score);
    // 最后根据总分决策
    if ($this->result->getScore() > 80) {
        $this->result->setDecision(Decision::REJECT);
    } elseif ($this->result->getScore() > 50) {
        $this->result->setDecision(Decision::REVIEW);
    }
  5. 性能优化

    • 规则缓存:将规则配置在项目启动时加载到内存(如PHP数组/Redis),避免每次请求读数据库。
    • 规则索引:根据事件类型(event_type)或规则条件中常用的字段(如user_idip)建立索引,减少需要匹配的规则数量。
    • 惰性特征计算:只计算规则确实用到的特征。

进阶与扩展

  1. 可视化规则编辑器

    • 后台用拖拽式界面生成规则(条件组合、动作配置),前端生成JSON/DSL,后端解析。
    • 支持 AND/OR 的树状组合。
  2. AB测试与灰度发布

    • 对规则可以设置 流量百分比 / 白名单用户,一部分流量走新规则,一部分走老逻辑,对比效果。
  3. 实时监控与告警

    监控风控调用量、命中率、规则执行耗时、Top N 风险用户/IP,当某类规则命中率突然飙升时通过钉钉/邮件告警。

  4. 支持脚本/DSL

    • 对于非常复杂的规则(如依赖循环计算、动态阈值),可以考虑嵌入 表达式引擎,如 symfony/expression-languageluasandbox(Lua沙箱),让规则能够执行一小段自定义逻辑。注意安全,严禁执行系统命令

一个简单的 operator 内部实现示例

<?php
interface OperatorInterface {
    public function evaluate(mixed $featureValue, mixed $ruleValue): bool;
}
class InListOperator implements OperatorInterface {
    public function evaluate(mixed $featureValue, mixed $ruleValue): bool {
        // ruleValue 通常是 JSON 字符串,如 "[1,2,3]"
        $list = json_decode($ruleValue, true);
        if (!is_array($list)) {
            return false;
        }
        return in_array($featureValue, $list, true);
    }
}
class RegexMatchOperator implements OperatorInterface {
    public function evaluate(mixed $featureValue, mixed $ruleValue): bool {
        // 注意:ruleValue 是正则表达式,需要做好安全过滤,避免ReDoS攻击
        // 使用 @ 抑制错误,或使用 try-catch
        try {
            return preg_match('/' . $ruleValue . '/', $featureValue) === 1;
        } catch (\Exception $e) {
            // 记录日志 LOG_WARN("Regex error: rule=%s, value=%s", $ruleValue, $featureValue);
            return false;
        }
    }
}
// 工厂类
class OperatorFactory {
    private static array $map = [
        'eq' => EqualOperator::class,
        'neq' => NotEqualOperator::class,
        'gt' => GreaterThanOperator::class,
        'in' => InListOperator::class,
        'match_regex' => RegexMatchOperator::class,
        // ...
    ];
    public static function create(string $operator): OperatorInterface {
        if (!isset(self::$map[$operator])) {
            throw new \InvalidArgumentException("Unknown operator: $operator");
        }
        return new self::$map[$operator]();
    }
}

一个好的PHP风控规则引擎设计,核心在于可配置化(DRY原则)高性能(缓存与惰性计算)易于扩展(策略模式/算子模式),不要一开始就追求大而全,可以先从最核心的规则执行引擎特征计算模块结果输出处理做起,配合一个简单的后台规则管理页面,就能应对大部分业务场景。

抱歉,评论功能暂时关闭!