php crm客户属性字段变更记录:业务合规与数据可信的核心保障

一、变更记录的核心价值

  1. 明确责任,避免扯皮
  • 客户等级、成交概率、联系方式、归属销售等变更都能追溯。
  • 出现问题时,能快速定位:谁操作、何时操作、改前改后。
  • 有效防止恶意修改、抢单、数据乱改等行为。
  1. 保证数据可信,支撑决策
  • 报表、业绩、客户分层等核心业务依赖字段数据。
  • 有变更记录,数据可审计、可回溯、可纠错。
  • 管理层可放心使用CRM数据进行业务决策。
  1. 合规与风控(尤其对公/金融/政企场景)
  • 满足内控、审计、监管等合规要求。
  • 重要信息(如客户资质、联系人、等级)必须留痕可查。
  • 纠纷、投诉、风控事件时,变更记录是关键证据。
  1. 还原客户生命周期,提升服务体验
  • 全程记录客户状态变化(潜在→跟进→成交)。
  • 需求、行业、预算、负责人等变更轨迹一目了然。
  • 便于交接、复盘、复购和客情维护。

二、标准变更记录应包含的信息

  • 客户 ID
  • 字段名(如客户等级、手机号、销售负责人等)
  • 旧值 / 新值
  • 操作人
  • 操作时间
  • 备注 / 修改原因(可选但强烈建议)

三、未做变更记录的风险

  • 数据随意修改,报表不可信
  • 销售扯皮、抢单、甩锅无法查证
  • 审计无法通过,存在合规风险
  • 客户交接混乱,历史信息断档
  • 出现问题只能"凭记忆",无法定位原因

一句话总结:客户属性字段变更记录 = CRM的数据黑匣子 + 业务责任链 + 合规底线。


四、操作记录日志表设计

sql 复制代码
CREATE TABLE `fa_customer_log` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `customer_id` int(10) unsigned DEFAULT NULL COMMENT '客户ID',
  `type` smallint(6) DEFAULT NULL COMMENT '类型',
  `batch_no` varchar(64) DEFAULT '' COMMENT '批量操作的唯一编号',
  `admin_id` int(10) unsigned DEFAULT NULL COMMENT '创建记录人',
  `memo` varchar(255) DEFAULT '' COMMENT '备注',
  `extra` text COMMENT '扩展参数',
  `ip` varchar(50) DEFAULT '' COMMENT 'ip地址',
  `source_id` int(10) unsigned DEFAULT NULL COMMENT '源ID',
  `create_date` datetime DEFAULT NULL COMMENT '创建时间',
  `update_date` datetime DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `idx_customerId` (`customer_id`),
  KEY `idx_createId` (`admin_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8mb4 COMMENT='客户变更记录';
  • batch_no:操作批次号,方便后续操作还原。

五、客户属性变更日志表设计

sql 复制代码
CREATE TABLE `fa_customer_profile_logs` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `customer_id` int(10) unsigned DEFAULT NULL COMMENT '客户ID',
  `field_name` varchar(100) DEFAULT '' COMMENT '字段值',
  `field_name_text` varchar(255) DEFAULT '' COMMENT '字段值名称',
  `before_field_value` varchar(1000) DEFAULT '' COMMENT '变更前值',
  `before_field_value_text` varchar(1000) DEFAULT '' COMMENT '变更前',
  `after_field_value` varchar(1000) DEFAULT '' COMMENT '变更后值',
  `after_field_value_text` varchar(1000) DEFAULT '' COMMENT '变更后',
  `source_id` int(10) DEFAULT NULL COMMENT '源ID',
  `create_date` datetime DEFAULT NULL COMMENT '创建时间',
  `admin_id` int(10) DEFAULT NULL COMMENT '创建人',
  PRIMARY KEY (`id`),
  KEY `idx_customerId` (`customer_id`),
  KEY `idx_field_name` (`field_name`),
  KEY `idx_source_id` (`source_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8mb4 COMMENT='客户属性变更记录';

六、客户模型中的变更记录实现(PHP示例)

1. 字段比较方法

php 复制代码
public function compareField($params = [])
{
    if (!$params) {
        $params = $this->getChangedData();
    }

    $originData = $this->origin;

    return $this->buildCompareData($params, $originData);
}

2. 构建比较数据

php 复制代码
public function buildCompareData($params, $originData)
{
    $table = $this->getTable();
    $fields = Db::query("SHOW FULL COLUMNS FROM {$table}");
    $compareFields = array_column($fields, 'Field');
    $fieldNames = array_column($fields, 'Comment', 'Field');

    $data = [];
    foreach ($params as $field => $value) {
        if (!\in_array($field, $compareFields)) {
            continue;
        }
        $oldOriginValue = $originData[$field] ?? '';
        $newOriginValue = $value;

        switch ($field) {
            case 'admin_id':
                $admin_user_list = Admin::whereIn('id', [$oldOriginValue, $newOriginValue])->column('username', 'id');
                $oldFieldText = $admin_user_list[$oldOriginValue] ?? '';
                $newFieldText = $admin_user_list[$newOriginValue] ?? '';
                break;
            case 'type':
                $type_list = CustomerEnum::typeList();
                $oldFieldText = $type_list[$oldOriginValue] ?? '';
                $newFieldText = $type_list[$newOriginValue] ?? '';
                break;
            default:
                $oldFieldText = $oldOriginValue;
                $newFieldText = $newOriginValue;
                break;
        }

        if ($oldOriginValue != $newOriginValue) {
            $data[$field] = [
                'field_name' => $field,
                'field_name_text' => $fieldNames[$field] ?? '',
                'before_field_value' => $oldOriginValue,
                'after_field_value' => $newOriginValue,
                'before_field_value_text' => $oldFieldText,
                'after_field_value_text' => $newFieldText,
            ];
        }
    }
    return $data;
}

3. 操作日志记录方法

php 复制代码
public static function logsRecord($type, $customerId, $memo, $compareData = [], $extra = [])
{
    $batch_no = $extra['batch_no'] ?? '';
    $source_id = $extra['source_id'] ?? null;
    unset($extra['batch_no'], $extra['source_id']);

    if (!$batch_no) {
        $batch_no = get_order_sn();
    }

    $customerLog = CustomerLog::create([
        'type' => $type,
        'customer_id' => $customerId,
        'memo' => $memo,
        'ip' => request()->ip(),
        'extra' => $extra,
        'batch_no' => $batch_no,
        'source_id' => $source_id,
        'admin_id' => session('admin.id'),
    ]);

    // 客户属性变更日志
    if ($compareData) {
        foreach ($compareData as $key => $item) {
            $item['source_id'] = $customerLog->id;
            $item['customer_id'] = $customerId;
            $item['admin_id'] = session('admin.id');
            $item['create_date'] = date('Y-m-d H:i:s');
            $compareData[$key] = $item;
        }
        CustomerProfileLog::insertAll($compareData);
    }
}

七、批量操作示例:从公海获取客户

php 复制代码
 $batch_no = get_order_sn();

foreach ($list as $item) {
    $data['admin_id'] = session('admin.id');
    $compareData = $item->compareField($data);

    $data['last_receive_date'] = date('Y-m-d H:i:s');
    $item->save($data);

    \app\admin\model\customer\Customer::logsRecord(
        CustomerLogEnum::TYPE_RECEIVE,
        $item->id,
        '从公海捞取',
        $compareData,
        [
            'batch_no' => $batch_no,
        ]
    );
}

相关推荐
阳光九叶草LXGZXJ6 小时前
达梦数据库-报错-16-MERGE INTO提示:无效的列名[XX]
linux·运维·数据库·sql·学习
Days20508 小时前
全网去水印1.0.0源码发布
php
镜舟科技8 小时前
从 StarRocks 到 MIP:数据平台的下一步是“理解”
starrocks·sql·ai·prompt·agent·context·mip
刘胡子大叔9 小时前
repo 与 index:站点目录该怎么切
ubuntu·php
2601_9628857210 小时前
批量取数比循环单取快多少?用 AlphaFeed klines.batch给多股票取数提速
开发语言·php·batch
迪康Defender11 小时前
终端安全事后追溯能力解析:本地审计模块设计思路与落地实践
运维·开发语言·人工智能·安全·php·安全威胁分析·运维开发
唐青枫11 小时前
别把 JWT 当成加密令牌:Symfony LexikJWTAuthenticationBundle 实战详解
php·symfony
专业程序开发源12 小时前
springboot古城景区管理系统88564-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·spring·php·课程设计
专业程序开发源13 小时前
springboot酒店会员点餐系统22339-计算机课程设计、毕业设计
java·javascript·vue.js·spring boot·后端·php·课程设计
搭贝1 天前
上厕所的时间搭好一套系统:AI自动生成实测
开发语言·人工智能·低代码·ai·php·搭贝