2025-06-19 15:14:41 +08:00
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
|
|
namespace app\cunkebao\controller\plan;
|
|
|
|
|
|
|
|
|
|
|
|
use library\ResponseHelper;
|
|
|
|
|
|
use think\Controller;
|
|
|
|
|
|
use think\Db;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 对外API接口控制器
|
|
|
|
|
|
*/
|
|
|
|
|
|
class PostExternalApiV1Controller extends Controller
|
|
|
|
|
|
{
|
2025-06-27 10:03:28 +08:00
|
|
|
|
|
2025-06-19 15:14:41 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 验证签名
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param array $params 请求参数
|
|
|
|
|
|
* @param string $apiKey API密钥
|
|
|
|
|
|
* @param string $sign 签名
|
|
|
|
|
|
* @return bool
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function validateSign($params, $apiKey, $sign)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 1. 从参数中移除sign和apiKey
|
2025-06-21 14:24:45 +08:00
|
|
|
|
unset($params['sign'], $params['apiKey'],$params['portrait']);
|
2025-06-19 15:14:41 +08:00
|
|
|
|
|
|
|
|
|
|
// 2. 移除空值
|
|
|
|
|
|
$params = array_filter($params, function($value) {
|
|
|
|
|
|
return !is_null($value) && $value !== '';
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 参数按键名升序排序
|
|
|
|
|
|
ksort($params);
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 直接拼接参数值
|
|
|
|
|
|
$stringToSign = implode('', array_values($params));
|
|
|
|
|
|
|
|
|
|
|
|
// 5. 第一次MD5加密
|
|
|
|
|
|
$firstMd5 = md5($stringToSign);
|
|
|
|
|
|
|
|
|
|
|
|
// 6. 拼接apiKey并第二次MD5加密
|
|
|
|
|
|
$expectedSign = md5($firstMd5 . $apiKey);
|
|
|
|
|
|
|
|
|
|
|
|
// 7. 比对签名
|
|
|
|
|
|
return $expectedSign === $sign;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 对外API接口入口
|
|
|
|
|
|
*
|
|
|
|
|
|
* @return \think\response\Json
|
|
|
|
|
|
*/
|
|
|
|
|
|
public function index()
|
|
|
|
|
|
{
|
|
|
|
|
|
try {
|
|
|
|
|
|
$params = $this->request->param();
|
|
|
|
|
|
|
|
|
|
|
|
// 验证必填参数
|
|
|
|
|
|
if (empty($params['apiKey'])) {
|
|
|
|
|
|
return ResponseHelper::error('apiKey不能为空', 400);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (empty($params['sign'])) {
|
|
|
|
|
|
return ResponseHelper::error('sign不能为空', 400);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (empty($params['timestamp'])) {
|
|
|
|
|
|
return ResponseHelper::error('timestamp不能为空', 400);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 验证时间戳(允许5分钟误差)
|
|
|
|
|
|
if (abs(time() - intval($params['timestamp'])) > 300) {
|
|
|
|
|
|
return ResponseHelper::error('请求已过期', 400);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 查询API密钥是否存在
|
|
|
|
|
|
$plan = Db::name('customer_acquisition_task')
|
|
|
|
|
|
->where('apiKey', $params['apiKey'])
|
|
|
|
|
|
->where('status', 1)
|
|
|
|
|
|
->find();
|
|
|
|
|
|
|
|
|
|
|
|
if (!$plan) {
|
|
|
|
|
|
return ResponseHelper::error('无效的apiKey', 401);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 验证签名
|
|
|
|
|
|
if (!$this->validateSign($params,$params['apiKey'], $params['sign'])) {
|
|
|
|
|
|
return ResponseHelper::error('签名验证失败', 401);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$identifier = !empty($params['wechatId']) ? $params['wechatId'] : $params['phone'];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$trafficPool = Db::name('traffic_pool')->where('identifier', $identifier)->find();
|
|
|
|
|
|
if (!$trafficPool) {
|
2025-06-21 14:24:45 +08:00
|
|
|
|
$trafficPoolId =Db::name('traffic_pool')->insertGetId([
|
2025-06-19 15:14:41 +08:00
|
|
|
|
'identifier' => $identifier,
|
2025-07-21 15:08:12 +08:00
|
|
|
|
'mobile' => !empty($params['phone']) ? $params['phone'] : '',
|
|
|
|
|
|
'createTime' => time()
|
2025-06-19 15:14:41 +08:00
|
|
|
|
]);
|
2025-06-21 14:24:45 +08:00
|
|
|
|
}else{
|
|
|
|
|
|
$trafficPoolId = $trafficPool['id'];
|
2025-06-19 15:14:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$taskCustomer = Db::name('task_customer')->where('task_id', $plan['id'])->where('phone', $identifier)->find();
|
2025-06-21 14:24:45 +08:00
|
|
|
|
|
|
|
|
|
|
// 处理用户画像
|
|
|
|
|
|
if(!empty($params['portrait']) && is_array($params['portrait'])){
|
2025-06-26 17:54:09 +08:00
|
|
|
|
$this->updatePortrait($params['portrait'],$trafficPoolId,$plan['companyId']);
|
2025-06-21 14:24:45 +08:00
|
|
|
|
}
|
2025-06-19 15:14:41 +08:00
|
|
|
|
if (!$taskCustomer) {
|
2025-06-20 17:05:39 +08:00
|
|
|
|
$tags = !empty($params['tags']) ? explode(',',$params['tags']) : [];
|
2025-06-26 17:15:50 +08:00
|
|
|
|
$siteTags = !empty($params['siteTags']) ? explode(',',$params['siteTags']) : [];
|
2025-06-19 15:14:41 +08:00
|
|
|
|
Db::name('task_customer')->insert([
|
|
|
|
|
|
'task_id' => $plan['id'],
|
2025-06-20 16:46:23 +08:00
|
|
|
|
'phone' => $identifier,
|
2025-06-20 17:23:23 +08:00
|
|
|
|
'name' => !empty($params['name']) ? $params['name'] : '',
|
2025-06-20 17:18:45 +08:00
|
|
|
|
'source' => !empty($params['source']) ? $params['source'] : '',
|
|
|
|
|
|
'remark' => !empty($params['remark']) ? $params['remark'] : '',
|
2025-06-20 16:46:23 +08:00
|
|
|
|
'tags' => json_encode($tags,256),
|
2025-06-26 17:15:50 +08:00
|
|
|
|
'siteTags' => json_encode($siteTags,256),
|
2025-07-28 17:40:50 +08:00
|
|
|
|
'createTime' => time(),
|
2025-06-19 15:14:41 +08:00
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
return json([
|
|
|
|
|
|
'code' => 200,
|
|
|
|
|
|
'message' => '新增成功',
|
|
|
|
|
|
'data' => $identifier
|
|
|
|
|
|
]);
|
|
|
|
|
|
}else{
|
2025-06-26 17:54:09 +08:00
|
|
|
|
$siteTags = !empty($params['siteTags']) ? explode(',',$params['siteTags']) : [];
|
|
|
|
|
|
|
|
|
|
|
|
// 更新新老标签数据,实现去重
|
|
|
|
|
|
$this->updateSiteTags($taskCustomer['id'], $siteTags);
|
|
|
|
|
|
|
2025-06-19 15:14:41 +08:00
|
|
|
|
return json([
|
|
|
|
|
|
'code' => 200,
|
|
|
|
|
|
'message' => '已存在',
|
|
|
|
|
|
'data' => $identifier
|
|
|
|
|
|
]);
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
|
return ResponseHelper::error('系统错误: ' . $e->getMessage(), 500);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-06-21 14:24:45 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 用户画像
|
|
|
|
|
|
* @param array $data 用户画像数据
|
|
|
|
|
|
* @param int $trafficPoolId 流量池id
|
|
|
|
|
|
*/
|
2025-06-26 17:54:09 +08:00
|
|
|
|
public function updatePortrait($data,$trafficPoolId,$companyId)
|
2025-06-21 14:24:45 +08:00
|
|
|
|
{
|
|
|
|
|
|
if(empty($data) || empty($trafficPoolId) || !is_array($data)){
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$type = !empty($data['type']) ? $data['type'] : 0;
|
|
|
|
|
|
$source = !empty($data['source']) ? $data['source'] : 0;
|
2025-06-25 11:42:26 +08:00
|
|
|
|
$sourceData = !empty($data['sourceData']) ? $data['sourceData'] : [];
|
2025-06-21 14:24:45 +08:00
|
|
|
|
$remark = !empty($data['remark']) ? $data['remark'] : '';
|
2025-06-25 14:28:42 +08:00
|
|
|
|
$uniqueId = !empty($data['uniqueId']) ? $data['uniqueId'] : 0;
|
2025-06-25 11:42:26 +08:00
|
|
|
|
ksort($sourceData);
|
|
|
|
|
|
$sourceData = json_encode($sourceData,256);
|
|
|
|
|
|
|
2025-06-21 14:24:45 +08:00
|
|
|
|
|
|
|
|
|
|
$data = [
|
2025-06-26 17:54:09 +08:00
|
|
|
|
'companyId' => $companyId,
|
2025-06-21 14:24:45 +08:00
|
|
|
|
'trafficPoolId' => $trafficPoolId,
|
|
|
|
|
|
'type' => $type,
|
|
|
|
|
|
'source' => $source,
|
2025-06-25 11:42:26 +08:00
|
|
|
|
'sourceData' => $sourceData,
|
2025-06-21 14:24:45 +08:00
|
|
|
|
'remark' => $remark,
|
2025-06-25 14:28:42 +08:00
|
|
|
|
'uniqueId' => $uniqueId,
|
2025-06-21 14:24:45 +08:00
|
|
|
|
'count' => 1,
|
|
|
|
|
|
'createTime' => time(),
|
|
|
|
|
|
'updateTime' => time(),
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
$res= Db::name('user_portrait')
|
2025-06-25 14:28:42 +08:00
|
|
|
|
->where(['trafficPoolId'=>$trafficPoolId,'type'=>$type,'source'=>$source,'uniqueId'=>$uniqueId])
|
2025-06-21 14:24:45 +08:00
|
|
|
|
->where('createTime','>',time()-1800)
|
|
|
|
|
|
->find();
|
|
|
|
|
|
if($res){
|
|
|
|
|
|
$count = $res['count'] + 1;
|
|
|
|
|
|
Db::name('user_portrait')->where(['id'=>$res['id']])->update(['count'=>$count,'updateTime'=>time()]);
|
|
|
|
|
|
}else{
|
|
|
|
|
|
Db::name('user_portrait')->insert($data);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
2025-06-26 17:54:09 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 更新站点标签数据,实现去重
|
|
|
|
|
|
* @param int $taskCustomerId 任务客户ID
|
|
|
|
|
|
* @param array $newSiteTags 新的站点标签数组
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function updateSiteTags($taskCustomerId, $newSiteTags)
|
|
|
|
|
|
{
|
2025-06-27 10:03:28 +08:00
|
|
|
|
|
2025-06-26 17:54:09 +08:00
|
|
|
|
if (empty($taskCustomerId) || empty($newSiteTags) || !is_array($newSiteTags)) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 获取当前任务客户的站点标签
|
|
|
|
|
|
$taskCustomer = Db::name('task_customer')->where('id', $taskCustomerId)->find();
|
2025-06-27 10:03:28 +08:00
|
|
|
|
|
2025-06-26 17:54:09 +08:00
|
|
|
|
if (!$taskCustomer) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 解析现有的站点标签
|
|
|
|
|
|
$existingSiteTags = [];
|
|
|
|
|
|
if (!empty($taskCustomer['siteTags'])) {
|
|
|
|
|
|
$existingSiteTags = json_decode($taskCustomer['siteTags'], true);
|
|
|
|
|
|
if (!is_array($existingSiteTags)) {
|
|
|
|
|
|
$existingSiteTags = [];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 合并新老标签并去重
|
|
|
|
|
|
$mergedSiteTags = array_merge($existingSiteTags, $newSiteTags);
|
|
|
|
|
|
$uniqueSiteTags = array_unique($mergedSiteTags);
|
|
|
|
|
|
|
|
|
|
|
|
// 过滤空值并重新索引数组
|
|
|
|
|
|
$uniqueSiteTags = array_values(array_filter($uniqueSiteTags, function($tag) {
|
|
|
|
|
|
return !empty(trim($tag));
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
2025-06-27 10:03:28 +08:00
|
|
|
|
|
2025-06-26 17:54:09 +08:00
|
|
|
|
// 更新数据库中的站点标签
|
|
|
|
|
|
Db::name('task_customer')->where('id', $taskCustomerId)->update([
|
|
|
|
|
|
'siteTags' => json_encode($uniqueSiteTags, JSON_UNESCAPED_UNICODE),
|
2025-07-17 10:15:49 +08:00
|
|
|
|
'updateTime' => time()
|
2025-06-26 17:54:09 +08:00
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
|
// 记录错误日志,但不影响主流程
|
|
|
|
|
|
\think\facade\Log::error('更新站点标签失败: ' . $e->getMessage());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-06-19 15:14:41 +08:00
|
|
|
|
}
|