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