新版数智员工

This commit is contained in:
wong
2026-03-24 10:39:16 +08:00
parent 1853934d85
commit 7fb61b77a4
123 changed files with 38491 additions and 1018 deletions

View File

@@ -2,92 +2,879 @@
namespace app\store\controller;
use app\common\controller\Api;
use app\common\model\TrafficPoolCompany;
use think\Db;
use think\facade\Log;
/**
* 客户管理控制器
*/
class CustomerController extends Api
class CustomerController extends BaseController
{
protected $noNeedLogin = [];
protected $noNeedRight = ['*'];
/**
* 获取客户列表
* GET /v2/store/customers
*
* @return \think\Response
* @return \think\response\Json
*/
public function getList()
{
$params = $this->request->param();
// 获取分页参数
$page = isset($params['page']) ? intval($params['page']) : 1;
$pageSize = isset($params['pageSize']) ? intval($params['pageSize']) : 10;
$userInfo = request()->userInfo;
$where = [];
// 必要的查询条件
$userId = $userInfo['id'];
$companyId = $userInfo['companyId'];
if (empty($userId) || empty($companyId)) {
return errorJson('缺少必要参数');
}
// 构建查询条件
$deviceIds = Db::name('device_user')->where(['userId' => $userId, 'companyId' => $companyId])->order('id DESC')->column('deviceId');
if (empty($deviceIds)) {
return errorJson('设备不存在');
}
$wechatIds = [];
foreach ($deviceIds as $deviceId) {
$wechatIds[] = Db::name('device_wechat_login')
->where(['deviceId' => $deviceId])
->order('id DESC')
->value('wechatId');
}
// 搜索条件
if (!empty($params['keyword'])) {
$where['alias|nickname|wechatId'] = ['like', '%' . $params['keyword'] . '%'];
}
// if (!empty($params['email'])) {
// $where['wa.bindEmail'] = ['like', '%' . $params['email'] . '%'];
// }
// if (!empty($params['name'])) {
// $where['wa.accountRealName|wa.accountUserName|wa.nickname'] = ['like', '%' . $params['name'] . '%'];
// }
// 构建查询
$query = Db::table('s2_wechat_friend')
->where($where)
->whereIn('ownerWechatId',$wechatIds)
->group('wechatId'); // 防止重复数据
try {
$userId = $this->userInfo['id'] ?? 0;
$companyId = $this->userInfo['companyId'] ?? 0;
// 克隆查询对象,用于计算总数
$countQuery = clone $query;
$total = $countQuery->count();
// 获取分页数据
$list = $query->page($page, $pageSize)
->order('id DESC')
->select();
// 格式化数据
foreach ($list as &$item) {
$item['labels'] = json_decode($item['labels'], true);
$item['createTime'] = date('Y-m-d H:i:s', $item['createTime']);
if (empty($userId) || empty($companyId)) {
return json(['code' => 401, 'msg' => '请先登录']);
}
// 获取设备信息
$device = $this->device;
if (empty($device) || empty($device['wechatId'])) {
return json(['code' => 404, 'msg' => '设备未绑定微信']);
}
$wechatId = $device['wechatId'];
// 获取微信账号ID
$wechatAccount = Db::table('s2_wechat_account')
->where('wechatId', $wechatId)
->field('id')
->find();
if (empty($wechatAccount)) {
return json(['code' => 404, 'msg' => '微信账号不存在']);
}
$accountId = $wechatAccount['id'];
// 分页参数
$page = intval($this->request->param('page', 1));
$limit = intval($this->request->param('limit', 10));
$pageSize = intval($this->request->param('pageSize', 10));
if ($page <= 0) $page = 1;
if ($limit <= 0) $limit = $pageSize > 0 ? $pageSize : 10;
if ($limit > 100) $limit = 100;
// 搜索关键词
$keyword = $this->request->param('keyword', '');
// 筛选条件
$status = $this->request->param('status', ''); // 状态:潜在、活跃、沉默、流失
$value = $this->request->param('value', ''); // 价值:高、中、低
$lifecycle = $this->request->param('lifecycle', ''); // 生命周期
// 构建查询条件
// 从流量池公司表查询,关联流量池总表和微信好友表
// 注意s2_wechat_friend 表没有 ck_ 前缀,使用数组形式 join 可以避免自动添加前缀
$query = Db::name('traffic_pool_company')
->alias('tpc')
->join('traffic_pool tp', 'tp.id = tpc.poolId', 'left')
->join(['s2_wechat_friend' => 'wf'], 'wf.wechatId = tp.wechatId AND wf.ownerWechatId = \'' . $wechatId . '\'', 'left')
->where([
['tpc.companyId', '=', $companyId],
['tpc.ownerAccountId', '=', $accountId], // 归属当前微信账号
['tpc.status', '=', TrafficPoolCompany::STATUS_NORMAL], // 正常状态
]);
// 关键词搜索(昵称、微信号、手机号)
if (!empty($keyword)) {
$query->where(function($query) use ($keyword) {
$query->where('tp.nickname', 'like', '%' . $keyword . '%')
->whereOr('tp.wechatAlias', 'like', '%' . $keyword . '%')
->whereOr('tp.mobile', 'like', '%' . $keyword . '%')
->whereOr('tpc.realName', 'like', '%' . $keyword . '%')
->whereOr('tpc.phone', 'like', '%' . $keyword . '%');
});
}
// 状态筛选(根据生命周期)
if (!empty($lifecycle)) {
$lifecycleMap = [
'潜在' => TrafficPoolCompany::LIFECYCLE_NEW,
'活跃' => TrafficPoolCompany::LIFECYCLE_FOLLOWING,
'沉默' => TrafficPoolCompany::LIFECYCLE_SILENT,
'流失' => TrafficPoolCompany::LIFECYCLE_LOST,
];
if (isset($lifecycleMap[$lifecycle])) {
$query->where('tpc.lifecycle', '=', $lifecycleMap[$lifecycle]);
}
}
// 价值筛选(根据意向度或等级)
if (!empty($value)) {
$valueMap = [
'高' => TrafficPoolCompany::INTENTION_HIGH,
'中' => TrafficPoolCompany::INTENTION_MEDIUM,
'低' => TrafficPoolCompany::INTENTION_LOW,
];
if (isset($valueMap[$value])) {
$query->where('tpc.intentionLevel', '=', $valueMap[$value]);
}
}
// 统计总数
$total = $query->count();
// 获取列表数据
$list = $query->field('tpc.id,tpc.poolId,tpc.companyId,tpc.ownerAccountId,tpc.realName,tpc.phone,tpc.email,tpc.lifecycle,tpc.intentionLevel,tpc.level,tpc.remark,tpc.createTime,tp.nickname,tp.avatar,tp.wechatId,tp.wechatAlias,tp.mobile,tp.gender,tp.region,tp.signature,wf.id as friendId,wf.alias as friendAlias,wf.nickname as friendNickname')
->order('tpc.id desc')
->page($page, $limit)
->select();
// 格式化数据
$result = [];
foreach ($list as $item) {
// 获取标签
$tags = Db::name('traffic_pool_tag')
->where([
['poolCompanyId', '=', $item['id']],
['isDel', '=', 0]
])
->column('tagName');
// 获取最后互动时间(从行为记录表)
$lastBehavior = Db::name('traffic_pool_behavior')
->where('poolCompanyId', $item['id'])
->order('behaviorTime desc')
->find();
$lastContact = '';
if (!empty($lastBehavior) && !empty($lastBehavior['behaviorTime'])) {
$lastContact = date('Y-m-d H:i:s', intval($lastBehavior['behaviorTime']));
}
// 获取价值评估从RFM或估值相关表这里先使用模拟数据
$valuation = $this->calculateCustomerValuation($item['id']);
// 状态映射
$lifecycleMap = [
TrafficPoolCompany::LIFECYCLE_NEW => '潜在',
TrafficPoolCompany::LIFECYCLE_FOLLOWING => '活跃',
TrafficPoolCompany::LIFECYCLE_CONVERTED => '已成交',
TrafficPoolCompany::LIFECYCLE_SILENT => '沉默',
TrafficPoolCompany::LIFECYCLE_LOST => '流失',
];
// 价值映射
$intentionMap = [
TrafficPoolCompany::INTENTION_HIGH => '高',
TrafficPoolCompany::INTENTION_MEDIUM => '中',
TrafficPoolCompany::INTENTION_LOW => '低',
TrafficPoolCompany::INTENTION_UNKNOWN => '低',
];
$result[] = [
'id' => intval($item['id']),
'poolCompanyId' => intval($item['id']),
'name' => $item['realName'] ?? $item['nickname'] ?? '未知',
'nickname' => $item['nickname'] ?? '',
'wechatId' => $item['wechatAlias'] ?? $item['wechatId'] ?? '',
'avatar' => $item['avatar'] ?? '',
'phone' => $item['phone'] ?? $item['mobile'] ?? '',
'email' => $item['email'] ?? '',
'status' => $lifecycleMap[$item['lifecycle'] ?? TrafficPoolCompany::LIFECYCLE_NEW] ?? '潜在',
'value' => $intentionMap[$item['intentionLevel'] ?? TrafficPoolCompany::INTENTION_UNKNOWN] ?? '低',
'tags' => $tags ?: [],
'lastContact' => $lastContact,
'nextFollow' => !empty($item['nextFollowTime']) && is_numeric($item['nextFollowTime'])
? date('Y-m-d', intval($item['nextFollowTime']))
: '',
'notes' => $item['remark'] ?? '',
'addedDate' => !empty($item['createTime']) && is_numeric($item['createTime'])
? date('Y-m-d', intval($item['createTime']))
: '',
'valuation' => $valuation,
];
}
return json([
'code' => 200,
'msg' => '获取成功',
'data' => [
'list' => $result,
'total' => $total,
'page' => $page,
'limit' => $limit
]
]);
} catch (\Exception $e) {
Log::error('获取客户列表失败: ' . $e->getMessage());
return json(['code' => 500, 'msg' => '获取失败:' . $e->getMessage()]);
}
unset($item);
return successJson([
'list' => $list,
'total' => $total
], '获取成功');
}
}
/**
* 获取客户详情
* GET /v2/store/customers/:id
*
* @return \think\response\Json
*/
public function detail()
{
try {
$userId = $this->userInfo['id'] ?? 0;
$companyId = $this->userInfo['companyId'] ?? 0;
if (empty($userId) || empty($companyId)) {
return json(['code' => 401, 'msg' => '请先登录']);
}
// 获取设备信息
$device = $this->device;
if (empty($device) || empty($device['wechatId'])) {
return json(['code' => 404, 'msg' => '设备未绑定微信']);
}
$wechatId = $device['wechatId'];
// 获取微信账号ID
$wechatAccount = Db::table('s2_wechat_account')
->where('wechatId', $wechatId)
->field('id')
->find();
if (empty($wechatAccount)) {
return json(['code' => 404, 'msg' => '微信账号不存在']);
}
$accountId = $wechatAccount['id'];
// 获取客户ID
$customerId = intval($this->request->param('id', 0));
if (empty($customerId)) {
return json(['code' => 400, 'msg' => '客户ID不能为空']);
}
// 查询客户详情
// 注意s2_wechat_friend 表没有 ck_ 前缀,使用数组形式 join 可以避免自动添加前缀
$customer = Db::name('traffic_pool_company')
->alias('tpc')
->join('traffic_pool tp', 'tp.id = tpc.poolId', 'left')
->join(['s2_wechat_friend' => 'wf'], 'wf.wechatId = tp.wechatId AND wf.ownerWechatId = \'' . $wechatId . '\'', 'left')
->where([
['tpc.id', '=', $customerId],
['tpc.companyId', '=', $companyId],
['tpc.ownerAccountId', '=', $accountId],
])
->field('tpc.*,tp.*,wf.id as friendId,wf.alias as friendAlias,wf.nickname as friendNickname')
->find();
if (empty($customer)) {
return json(['code' => 404, 'msg' => '客户不存在']);
}
// 获取标签
$tags = Db::name('traffic_pool_tag')
->where([
['poolCompanyId', '=', $customerId],
['isDel', '=', 0]
])
->column('tagName');
// 获取流量池标签(系统标签或微信标签)
// 注意从表结构看isSystem字段在tagDefineId关联的标签定义表中
// 这里先获取所有标签后续可以根据tagType区分
$allTags = Db::name('traffic_pool_tag')
->alias('tpt')
->join('traffic_pool_tag_define tptd', 'tpt.tagDefineId = tptd.id', 'left')
->where([
['tpt.poolCompanyId', '=', $customerId],
['tpt.isDel', '=', 0]
])
->field('tpt.tagName,tptd.isSystem')
->select();
$trafficPoolTags = [];
foreach ($allTags as $tag) {
// 系统标签或微信标签tagType=1作为流量池标签
if (!empty($tag['isSystem']) || (!empty($tag['tagType']) && $tag['tagType'] == 1)) {
$trafficPoolTags[] = $tag['tagName'];
}
}
// 获取来源信息
$sources = Db::name('traffic_pool_source')
->where('poolCompanyId', $customerId)
->order('createTime desc')
->select();
$sourceChannel = '未知';
$addTime = '';
if (!empty($sources)) {
$firstSource = $sources[0];
$sourceChannel = $firstSource['sourceName'] ?? '未知';
$addTime = !empty($firstSource['createTime']) && is_numeric($firstSource['createTime'])
? date('Y-m-d', intval($firstSource['createTime']))
: '';
}
// 获取互动统计
$interactionStats = $this->getInteractionStats($customerId);
// 获取价值评估
$valueEvaluation = $this->getValueEvaluation($customerId);
// 获取用户旅程(最近记录)
$journey = $this->getCustomerJourney($customerId, 10);
// 获取消费偏好(从行为记录分析)
$preferences = $this->getCustomerPreferences($customerId);
// 状态映射
$lifecycleMap = [
TrafficPoolCompany::LIFECYCLE_NEW => '潜在',
TrafficPoolCompany::LIFECYCLE_FOLLOWING => '活跃',
TrafficPoolCompany::LIFECYCLE_CONVERTED => '已成交',
TrafficPoolCompany::LIFECYCLE_SILENT => '沉默',
TrafficPoolCompany::LIFECYCLE_LOST => '流失',
];
$conversionStatus = $lifecycleMap[$customer['lifecycle'] ?? TrafficPoolCompany::LIFECYCLE_NEW] ?? '潜在';
// 生成首字母
$name = $customer['realName'] ?? $customer['nickname'] ?? '未知';
$initials = mb_substr($name, 0, 1, 'UTF-8');
return json([
'code' => 200,
'msg' => '获取成功',
'data' => [
// 基础信息
'id' => intval($customer['id']),
'poolCompanyId' => intval($customer['id']),
'initials' => $initials,
// 好友概览
'nickname' => $customer['nickname'] ?? '',
'remarkName' => $customer['realName'] ?? '',
'wechatId' => $customer['wechatAlias'] ?? $customer['wechatId'] ?? '',
'wechatPhone' => $customer['mobile'] ?? '',
'wechatLocation' => $customer['region'] ?? '',
'avatar' => $customer['avatar'] ?? '',
'conversionStatus' => $conversionStatus,
'sourceChannel' => $sourceChannel,
'addTime' => $addTime,
// 基础信息
'realName' => $customer['realName'] ?? '',
'sex' => $this->getGenderText($customer['gender'] ?? 0),
'age' => $this->calculateAge($customer['birthday'] ?? ''),
'personalPhone' => $customer['phone'] ?? '',
'email' => $customer['email'] ?? '',
'idNumber' => $this->maskIdNumber($customer['idCard'] ?? ''),
'address' => $customer['address'] ?? '',
// 标签
'tags' => $tags ?: [],
'trafficPoolTags' => $trafficPoolTags ?: [],
// 互动统计
'interactionStats' => $interactionStats,
// 价值评估
'valueEvaluation' => $valueEvaluation,
'valuationRank' => 'TOP 8%', // 需要计算
'valuationTrend' => '+12%', // 需要计算
// 用户旅程
'journey' => $journey,
// 消费偏好
'preferences' => $preferences,
// AI预测需要实现
'aiProfile' => [
'summary' => '该用户为典型的高净值客户,消费频率高且偏好高端产品。',
'predictions' => [
'预计未来7天内有85%概率下单',
'流失风险极低5%),建议通过会员活动维持粘性',
'最佳触达时间工作日12:00-14:00或周末下午'
]
],
]
]);
} catch (\Exception $e) {
Log::error('获取客户详情失败: ' . $e->getMessage());
return json(['code' => 500, 'msg' => '获取失败:' . $e->getMessage()]);
}
}
/**
* 更新客户信息
* PUT /v2/store/customers/:id
*
* @return \think\response\Json
*/
public function update()
{
try {
$userId = $this->userInfo['id'] ?? 0;
$companyId = $this->userInfo['companyId'] ?? 0;
if (empty($userId) || empty($companyId)) {
return json(['code' => 401, 'msg' => '请先登录']);
}
// 获取设备信息
$device = $this->device;
if (empty($device) || empty($device['wechatId'])) {
return json(['code' => 404, 'msg' => '设备未绑定微信']);
}
$wechatId = $device['wechatId'];
// 获取微信账号ID
$wechatAccount = Db::table('s2_wechat_account')
->where('wechatId', $wechatId)
->field('id')
->find();
if (empty($wechatAccount)) {
return json(['code' => 404, 'msg' => '微信账号不存在']);
}
$accountId = $wechatAccount['id'];
// 获取客户ID
$customerId = intval($this->request->param('id', 0));
if (empty($customerId)) {
return json(['code' => 400, 'msg' => '客户ID不能为空']);
}
// 验证客户是否存在且归属当前账号
$customer = Db::name('traffic_pool_company')
->where([
['id', '=', $customerId],
['companyId', '=', $companyId],
['ownerAccountId', '=', $accountId],
])
->find();
if (empty($customer)) {
return json(['code' => 404, 'msg' => '客户不存在']);
}
// 获取更新参数
$updateType = $this->request->param('updateType', ''); // wechat, personal, tags
$updateData = [];
$updateFields = [];
// 更新微信资料
if ($updateType === 'wechat' || $this->request->has('remarkName')) {
$remarkName = $this->request->param('remarkName', '');
if ($remarkName !== '') {
$updateData['realName'] = $remarkName; // 备注名存储在realName字段
$updateFields[] = '备注名';
}
}
// 更新基础信息
if ($updateType === 'personal') {
$realName = $this->request->param('realName', '');
$sex = $this->request->param('sex', '');
$age = $this->request->param('age', '');
$phone = $this->request->param('phone', '');
$email = $this->request->param('email', '');
$idNumber = $this->request->param('idNumber', '');
$address = $this->request->param('address', '');
if ($realName !== '') {
$updateData['realName'] = $realName;
$updateFields[] = '姓名';
}
if ($sex !== '') {
$updateData['gender'] = $sex === '男' ? 1 : ($sex === '女' ? 2 : 0);
$updateFields[] = '性别';
}
if ($age !== '') {
// 根据年龄计算生日(简化处理)
$birthYear = date('Y') - intval($age);
$updateData['birthday'] = $birthYear . '-01-01';
$updateFields[] = '年龄';
}
if ($phone !== '') {
$updateData['phone'] = $phone;
$updateFields[] = '手机号';
}
if ($email !== '') {
$updateData['email'] = $email;
$updateFields[] = '邮箱';
}
if ($idNumber !== '') {
$updateData['idCard'] = $idNumber;
$updateFields[] = '身份证号';
}
if ($address !== '') {
$updateData['address'] = $address;
$updateFields[] = '住址';
}
}
// 更新标签
if ($updateType === 'tags') {
$tags = $this->request->param('tags', []);
if (is_array($tags)) {
// 获取客户信息用于获取identifier和companyId
$customerInfo = Db::name('traffic_pool_company')
->where('id', $customerId)
->field('identifier,companyId')
->find();
if (!empty($customerInfo)) {
// 软删除旧标签(只删除站内标签,保留微信标签和系统标签)
// 通过关联标签定义表判断是否为站内标签
Db::name('traffic_pool_tag')
->alias('tpt')
->join('traffic_pool_tag_define tptd', 'tpt.tagDefineId = tptd.id', 'left')
->where([
['tpt.poolCompanyId', '=', $customerId],
['tptd.tagType', '=', 2], // 站内标签
['tpt.isDel', '=', 0]
])
->update([
'tpt.isDel' => 1,
'tpt.deleteTime' => time()
]);
// 添加新标签(站内标签)
foreach ($tags as $tag) {
if (!empty($tag)) {
// 查找或创建标签定义
$tagDefine = Db::name('traffic_pool_tag_define')
->where([
['companyId', 'in', [$companyId, 0]],
['tagName', '=', $tag],
['tagType', '=', 2], // 站内标签
['isDel', '=', 0]
])
->order('companyId desc') // 优先使用公司自定义标签
->find();
if (empty($tagDefine)) {
// 创建标签定义
$tagDefineId = Db::name('traffic_pool_tag_define')->insertGetId([
'companyId' => $companyId,
'tagType' => 2, // 站内标签
'tagCode' => 'custom_' . time() . '_' . rand(1000, 9999),
'tagName' => $tag,
'isSystem' => 0,
'status' => 1,
'createTime' => time(),
]);
} else {
$tagDefineId = $tagDefine['id'];
}
// 检查标签是否已存在
$existTag = Db::name('traffic_pool_tag')
->where([
['poolCompanyId', '=', $customerId],
['tagDefineId', '=', $tagDefineId],
['isDel', '=', 0]
])
->find();
if (empty($existTag)) {
Db::name('traffic_pool_tag')->insert([
'poolCompanyId' => $customerId,
'identifier' => $customerInfo['identifier'],
'companyId' => $customerInfo['companyId'],
'tagDefineId' => $tagDefineId,
'tagType' => 2, // 站内标签
'tagName' => $tag,
'source' => 1, // 手动
'operatorId' => $userId,
'createTime' => time(),
]);
}
}
}
}
$updateFields[] = '标签';
}
}
// 更新客户信息
if (!empty($updateData)) {
$updateData['updateTime'] = time();
Db::name('traffic_pool_company')
->where('id', $customerId)
->update($updateData);
}
return json([
'code' => 200,
'msg' => '更新成功',
'data' => [
'updatedFields' => $updateFields
]
]);
} catch (\Exception $e) {
Log::error('更新客户信息失败: ' . $e->getMessage());
return json(['code' => 500, 'msg' => '更新失败:' . $e->getMessage()]);
}
}
/**
* 计算客户估值
*
* @param int $poolCompanyId 客户ID
* @return int
*/
private function calculateCustomerValuation($poolCompanyId)
{
// TODO: 实现真实的估值计算逻辑
// 可以从订单表、行为记录表等计算
return 50000; // 模拟数据
}
/**
* 获取互动统计
*
* @param int $poolCompanyId 客户ID
* @return array
*/
private function getInteractionStats($poolCompanyId)
{
// 统计聊天消息数
$chatCount = Db::name('traffic_pool_behavior')
->where([
['poolCompanyId', '=', $poolCompanyId],
['behaviorType', '=', 1] // 发送消息
])
->count();
// 统计朋友圈互动数
$momentsCount = Db::name('traffic_pool_behavior')
->where([
['poolCompanyId', '=', $poolCompanyId],
['behaviorType', 'in', [9, 10]] // 点赞朋友圈、评论朋友圈
])
->count();
// 统计红包转账总额(从行为记录中获取)
$redPacketTotal = Db::name('traffic_pool_behavior')
->where([
['poolCompanyId', '=', $poolCompanyId],
['behaviorType', '=', 7] // 支付
])
->sum('amount');
$redPacketTotal = round(floatval($redPacketTotal ?? 0), 2);
// 计算活跃度评分(简化计算)
$activeScore = min(100, ($chatCount * 2 + $momentsCount * 3 + $redPacketTotal / 10));
// 获取最后互动时间
$lastBehavior = Db::name('traffic_pool_behavior')
->where('poolCompanyId', $poolCompanyId)
->order('behaviorTime desc')
->find();
$lastInteraction = '从未互动';
if (!empty($lastBehavior) && !empty($lastBehavior['behaviorTime'])) {
$time = intval($lastBehavior['behaviorTime']);
$diff = time() - $time;
if ($diff < 3600) {
$lastInteraction = '刚刚';
} elseif ($diff < 86400) {
$lastInteraction = '今天 ' . date('H:i', $time);
} elseif ($diff < 172800) {
$lastInteraction = '昨天 ' . date('H:i', $time);
} else {
$lastInteraction = date('Y-m-d H:i', $time);
}
}
return [
'lastInteraction' => $lastInteraction,
'chatCount' => intval($chatCount),
'momentsCount' => intval($momentsCount),
'redPacketTotal' => number_format($redPacketTotal, 2),
'activeScore' => intval($activeScore)
];
}
/**
* 获取价值评估
*
* @param int $poolCompanyId 客户ID
* @return array
*/
private function getValueEvaluation($poolCompanyId)
{
// TODO: 实现真实的价值评估计算
// 可以从RFM模型、CLV模型、社交裂变模型等计算
return [
'totalValuation' => 58600,
'models' => [
[
'name' => 'RFM 贡献模型',
'value' => 52000,
'weight' => 0.5,
'score' => 92
],
[
'name' => 'CLV 终身价值模型',
'value' => 78000,
'weight' => 0.3,
'score' => 88
],
[
'name' => '社交/裂变模型',
'value' => 15000,
'weight' => 0.2,
'score' => 75
]
]
];
}
/**
* 获取用户旅程
*
* @param int $poolCompanyId 客户ID
* @param int $limit 限制数量
* @return array
*/
private function getCustomerJourney($poolCompanyId, $limit = 10)
{
// 从行为记录表获取
$behaviors = Db::name('traffic_pool_behavior')
->where('poolCompanyId', $poolCompanyId)
->order('behaviorTime desc')
->limit($limit)
->select();
$journey = [];
$typeMap = [
1 => '发送消息',
2 => '接收消息',
3 => '浏览',
4 => '点击',
5 => '咨询',
6 => '下单',
7 => '支付',
8 => '退款',
9 => '点赞朋友圈',
10 => '评论朋友圈',
];
foreach ($behaviors as $behavior) {
$type = $typeMap[$behavior['behaviorType']] ?? '未知行为';
$content = $behavior['behaviorName'] ?? $type;
if (!empty($behavior['targetName'])) {
$content .= ': ' . $behavior['targetName'];
}
$journey[] = [
'type' => $type,
'content' => $content,
'time' => !empty($behavior['behaviorTime']) && is_numeric($behavior['behaviorTime'])
? date('Y-m-d H:i:s', intval($behavior['behaviorTime']))
: '',
'source' => '存客宝',
'actionType' => $this->getActionType($behavior['behaviorType']),
'amount' => !empty($behavior['amount']) && floatval($behavior['amount']) > 0
? '¥' . number_format(floatval($behavior['amount']), 2)
: '',
];
}
return $journey;
}
/**
* 获取行为类型
*
* @param int $behaviorType 行为类型
* @return string
*/
private function getActionType($behaviorType)
{
if (in_array($behaviorType, [6, 7, 8])) {
return 'transaction'; // 交易
} elseif (in_array($behaviorType, [1, 2, 9, 10])) {
return 'social'; // 社交
} elseif (in_array($behaviorType, [3, 4, 5])) {
return 'footprint'; // 轨迹
} else {
return 'flow'; // 流量
}
}
/**
* 获取消费偏好
*
* @param int $poolCompanyId 客户ID
* @return array
*/
private function getCustomerPreferences($poolCompanyId)
{
// TODO: 从行为记录和订单记录分析消费偏好
return [
'categories' => ['智能数码', '精品咖啡', '商务休闲'],
'recentItems' => ['iPhone 16 Pro', 'iPad Air'],
'coreInterest' => '数码发烧友 & 品质生活追求者'
];
}
/**
* 获取性别文本
*
* @param int $gender 性别代码
* @return string
*/
private function getGenderText($gender)
{
$map = [
0 => '保密',
1 => '男',
2 => '女',
];
return $map[$gender] ?? '未知';
}
/**
* 计算年龄
*
* @param string $birthday 生日
* @return int
*/
private function calculateAge($birthday)
{
if (empty($birthday)) {
return 0;
}
$birthTimestamp = strtotime($birthday);
if ($birthTimestamp === false) {
return 0;
}
$age = date('Y') - date('Y', $birthTimestamp);
if (date('md', $birthTimestamp) > date('md')) {
$age--;
}
return $age;
}
/**
* 脱敏身份证号
*
* @param string $idNumber 身份证号
* @return string
*/
private function maskIdNumber($idNumber)
{
if (empty($idNumber) || strlen($idNumber) < 8) {
return $idNumber;
}
return substr($idNumber, 0, 4) . str_repeat('*', strlen($idNumber) - 8) . substr($idNumber, -4);
}
}