PHP项目AI审核结果如何人工复核流转

wen PHP项目 26

本文目录导读:

PHP项目AI审核结果如何人工复核流转

  1. 数据库表结构设计(核心)
  2. 状态流转设计
  3. PHP 核心逻辑实现
  4. AI 审核回调(触发入口)
  5. 用户界面交互建议
  6. 注意事项(坑点)

在PHP项目中实现AI审核结果的人工复核流转,核心思路是将“AI自动判断”与“人工干预”解耦,并通过状态机或工作流引擎来管理审核状态

以下是实现该功能的完整方案,分为数据结构设计状态流转逻辑关键代码实现UI交互建议四个部分。

数据库表结构设计(核心)

需要两张核心表:audit_records(审核记录表)和audit_workflow_log(审核日志表)。

表1:audit_records(审核记录表)

CREATE TABLE `audit_records` (
    `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    `target_type` VARCHAR(50) NOT NULL COMMENT '审核对象类型,如:article, comment, image',  
    `target_id` INT UNSIGNED NOT NULL COMMENT '审核对象ID',
    `content` TEXT COMMENT '原始内容摘要,方便人工查看',
    // AI 审核结果
    `ai_result` ENUM('pass', 'reject', 'review') DEFAULT 'review' COMMENT 'AI判定结果:通过/拒绝/疑似需人工审核',
    `ai_reason` VARCHAR(500) DEFAULT NULL COMMENT 'AI驳回原因或风险标签',
    `ai_score` DECIMAL(5,2) DEFAULT NULL COMMENT 'AI置信度分数,用于辅助判断',
    // 流转控制字段(关键)
    `status` ENUM('pending_ai', 'pending_review', 'passed', 'rejected') DEFAULT 'pending_ai' COMMENT '当前审核状态',
    `assigned_to` INT UNSIGNED DEFAULT NULL COMMENT '当前审核人ID(抢单/分配)',
    `assigned_at` DATETIME DEFAULT NULL COMMENT '分配时间',
    `reviewed_by` INT UNSIGNED DEFAULT NULL COMMENT '最终复核人ID',
    `review_result` ENUM('pass', 'reject') DEFAULT NULL COMMENT '人工最终结果',
    `review_reason` TEXT COMMENT '人工备注',
    // 时间戳
    `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
    `updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX `idx_target` (`target_type`, `target_id`),
    INDEX `idx_status` (`status`),
    INDEX `idx_assigned_to` (`assigned_to`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

表2:audit_workflow_log(审核流转日志)

CREATE TABLE `audit_workflow_log` (
    `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    `record_id` INT UNSIGNED NOT NULL,
    `from_status` VARCHAR(50) DEFAULT NULL,
    `to_status` VARCHAR(50) NOT NULL,
    `action` VARCHAR(100) NOT NULL COMMENT '操作:ai_auto / assign / review_pass / review_reject / recall',
    `operator_id` INT UNSIGNED DEFAULT NULL COMMENT '操作人ID(null表示系统)',
    `operator_name` VARCHAR(100) DEFAULT NULL,
    `remark` TEXT COMMENT '操作备注',
    `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
    INDEX `idx_record` (`record_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

状态流转设计

设计一个有限状态机(FSM),避免乱序操作,推荐使用枚举或常量类管理:

当前状态 可执行动作 下一状态 说明
pending_ai AI 审核完成 pending_review(AI 结果为 review) AI 认为需要人工复核
pending_ai AI 审核完成 passed(AI 结果为 pass) AI 直接通过(可选)
pending_ai AI 审核完成 rejected(AI 结果为 reject) AI 直接拒绝(可选)
pending_review 人工复核通过 passed 核心流转点
pending_review 人工复核拒绝 rejected 核心流转点
pending_review 手动分配/抢单 pending_review (更新assigned_to) 分配审核人
passed 撤回操作(如果需要) pending_review 特殊情况,如误判
rejected 撤回操作(如果需要) pending_review 特殊情况

PHP 核心逻辑实现

建议封装一个单独的“审核工作流服务类”。

<?php
// app/Services/AuditWorkflowService.php
class AuditWorkflowService
{
    // 定义允许的状态转换
    private array $transitions = [
        'pending_ai' => ['pending_review', 'passed', 'rejected'],
        'pending_review' => ['passed', 'rejected'],
        'passed' => ['pending_review'], // 撤回
        'rejected' => ['pending_review'], // 撤回
    ];
    /**
     * 执行状态变更(核心方法)
     * @param int $recordId 审核记录ID
     * @param string $action 操作动作
     * @param int|null $operatorId 操作人ID
     * @param string|null $remark 备注
     * @param array $extraData 额外数据(如 review_result, review_reason)
     * @return bool
     * @throws \Exception
     */
    public function transition(int $recordId, string $action, ?int $operatorId = null, ?string $remark = null, array $extraData = []): bool
    {
        DB::beginTransaction();
        try {
            $record = AuditRecord::lockForUpdate()->find($recordId);
            if (!$record) throw new \Exception('审核记录不存在');
            $currentStatus = $record->status;
            // 根据action 计算出目标状态
            $newStatus = $this->getNextStatus($currentStatus, $action);
            // 校验转换是否合法 (防止直接修改数据库)
            if (!in_array($newStatus, $this->transitions[$currentStatus] ?? [])) {
                throw new \Exception("不允许从 {$currentStatus} 转换到 {$newStatus}");
            }
            // 特殊校验:分配操作
            if ($action === 'assign') {
                $this->assignHandler($record, $extraData['assigned_to'] ?? 0);
            }
            // 更新主记录
            $updateData = [
                'status' => $newStatus,
                'updated_at' => now(),
            ];
            // 如果是人工最终审核(通过/拒绝),写入人工结果
            if (in_array($newStatus, ['passed', 'rejected'])) {
                $updateData['reviewed_by'] = $operatorId;
                $updateData['review_result'] = ($newStatus === 'passed') ? 'pass' : 'reject';
                $updateData['review_reason'] = $remark;
            }
            $record->update($updateData);
            // 写流转日志
            $this->logTransition($recordId, $currentStatus, $newStatus, $action, $operatorId, $remark);
            DB::commit();
            return true;
        } catch (\Exception $e) {
            DB::rollBack();
            throw $e;
        }
    }
    private function getNextStatus(string $currentStatus, string $action): string
    {
        // 根据动作映射新状态
        $map = [
            'pass' => 'passed',
            'reject' => 'rejected',
            'assign' => 'pending_review', // 分配后依然是待审状态
            'recall' => 'pending_review', // 撤回
        ];
        return $map[$action] ?? $currentStatus;
    }
    private function assignHandler($record, int $assignedTo): void
    {
        // 这里可以增加并发控制,防止多人同时抢同一单
        if ($record->assigned_to && $record->assigned_to != $assignedTo) {
            throw new \Exception('该记录已被其他审核员认领');
        }
        $record->update([
            'assigned_to' => $assignedTo,
            'assigned_at' => now(),
        ]);
    }
    private function logTransition($recordId, $fromStatus, $toStatus, $action, $operatorId, $remark): void
    {
        AuditWorkflowLog::create([
            'record_id' => $recordId,
            'from_status' => $fromStatus,
            'to_status' => $toStatus,
            'action' => $action,
            'operator_id' => $operatorId,
            'operator_name' => $operatorId ? User::find($operatorId)->name : 'system',
            'remark' => $remark,
        ]);
    }
    // --- 对外API ---
    /**
     * 人工审核通过
     */
    public function reviewPass(int $recordId, int $operatorId, string $reason = '')
    {
        return $this->transition($recordId, 'pass', $operatorId, $reason);
    }
    /**
     * 人工审核拒绝
     */
    public function reviewReject(int $recordId, int $operatorId, string $reason = '')
    {
        return $this->transition($recordId, 'reject', $operatorId, $reason);
    }
    /**
     * 认领/分配
     */
    public function assign(int $recordId, int $operatorId)
    {
        return $this->transition($recordId, 'assign', $operatorId, '认领任务', ['assigned_to' => $operatorId]);
    }
}

AI 审核回调(触发入口)

在你的AI回调逻辑中,调用工作流服务。

// 假设在AI审核回调中
public function handleAiCallback($targetType, $targetId, $aiResult, $aiReason, $aiScore)
{
    // 1. 查找或创建审核记录
    $record = AuditRecord::firstOrCreate(
        ['target_type' => $targetType, 'target_id' => $targetId],
        ['content' => '...', 'status' => 'pending_ai']
    );
    // 2. 写入AI结果
    $record->update([
        'ai_result' => $aiResult,
        'ai_reason' => $aiReason,
        'ai_score' => $aiScore,
    ]);
    // 3. 根据AI结果决定下一步流转
    $workflow = new AuditWorkflowService();
    switch ($aiResult) {
        case 'pass':
            $workflow->transition($record->id, 'auto_pass', null, 'AI自动通过, 分数:'.$aiScore);
            break;
        case 'reject':
            $workflow->transition($record->id, 'auto_reject', null, 'AI自动拒绝:'.$aiReason);
            break;
        case 'review':
            // 这里可以触发消息通知,提醒审核员有新任务
            $workflow->transition($record->id, 'need_review', null, 'AI判定需人工复审');
            break;
    }
}

用户界面交互建议

  1. 待审列表:根据 status = 'pending_review' 过滤,列表显示 AI 评分、AI 判定原因(黄/红色标签提示),方便人工优先级排序。
  2. 详情页面
    • 顶部:展示 AI Result(绿色/红色徽章)和 AI Reason
    • 中间:展示原始内容(文本/图片)。
    • 底部:审核操作按钮(通过 / 拒绝),并强制填写理由。
  3. 抢单机制:可以加一个 assigned_to 字段,不为空的记录在其他人列表中不显示,利用数据库事务 + 行锁防止重复分配。
  4. 批量处理:在列表页提供多选框 + 批量通过/拒绝(注意:批量操作也要走循环调用 transition,因为事务和锁的开销)。
  5. 撤回功能:仅开放给超级管理员,用于纠正误操作。

注意事项(坑点)

  1. 并发问题:审核员可能同时点击“通过”,日志里的 lockForUpdate() 是必要的,防止状态错乱。
  2. 消息通知:当 AI 标记为 review 时,应通过 WebSocket 或消息队列通知相关负责人。
  3. 批量操作:不要直接用 update() 跳过日志,即使批量通过/拒绝,也应该逐条调用 transition 记录日志。
  4. 撤回风险:被撤回的记录,内容中应保留原始 AI 和人工的审核历史,不能丢失。
  5. 可视化流程图:如果能用 laravel-workflow 或类似库画一个流程图更好,但一般的 PHPer 直接用上面状态机足够了。

方案的核心是:status 字段 + audit_workflow_log 日志 + lockForUpdate 并发控制,通过简单的 switch-case状态数组 来定义流转规则,可维护性高,不易出错。

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