From d40710de3c3db790053cd4b02034fb9caed7f538 Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Tue, 12 Aug 2025 10:04:53 +0800 Subject: [PATCH 01/78] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/plan/GetAddFriendPlanDetailV1Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Server/application/cunkebao/controller/plan/GetAddFriendPlanDetailV1Controller.php b/Server/application/cunkebao/controller/plan/GetAddFriendPlanDetailV1Controller.php index 27b6876f..53deaaf4 100644 --- a/Server/application/cunkebao/controller/plan/GetAddFriendPlanDetailV1Controller.php +++ b/Server/application/cunkebao/controller/plan/GetAddFriendPlanDetailV1Controller.php @@ -118,7 +118,7 @@ class GetAddFriendPlanDetailV1Controller extends Controller // 解析JSON字段 $sceneConf = json_decode($plan['sceneConf'], true) ?: []; - $sceneConf['wechatGroups'] = $sceneConf['groupSelected']; + $sceneConf['wechatGroups'] = !empty($sceneConf['groupSelected']) ? $sceneConf['groupSelected'] : []; $reqConf = json_decode($plan['reqConf'], true) ?: []; $reqConf['deveiceGroups'] = $reqConf['device']; $msgConf = json_decode($plan['msgConf'], true) ?: []; From 67fac4b2f25f08338d69af702684aefdad8679a7 Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Tue, 12 Aug 2025 15:02:00 +0800 Subject: [PATCH 02/78] =?UTF-8?q?=E5=9C=BA=E6=99=AF=E8=8E=B7=E5=AE=A2?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../GetAddFriendPlanDetailV1Controller.php | 6 +- .../PostCreateAddFriendPlanV1Controller.php | 77 ++++++++++++++++++- .../PostUpdateAddFriendPlanV1Controller.php | 15 ++-- 3 files changed, 82 insertions(+), 16 deletions(-) diff --git a/Server/application/cunkebao/controller/plan/GetAddFriendPlanDetailV1Controller.php b/Server/application/cunkebao/controller/plan/GetAddFriendPlanDetailV1Controller.php index 53deaaf4..04ffac30 100644 --- a/Server/application/cunkebao/controller/plan/GetAddFriendPlanDetailV1Controller.php +++ b/Server/application/cunkebao/controller/plan/GetAddFriendPlanDetailV1Controller.php @@ -118,19 +118,19 @@ class GetAddFriendPlanDetailV1Controller extends Controller // 解析JSON字段 $sceneConf = json_decode($plan['sceneConf'], true) ?: []; - $sceneConf['wechatGroups'] = !empty($sceneConf['groupSelected']) ? $sceneConf['groupSelected'] : []; $reqConf = json_decode($plan['reqConf'], true) ?: []; $reqConf['deveiceGroups'] = $reqConf['device']; $msgConf = json_decode($plan['msgConf'], true) ?: []; $tagConf = json_decode($plan['tagConf'], true) ?: []; + if(!empty($sceneConf['wechatGroups'])){ $groupList = Db::name('wechat_group')->alias('wg') ->join('wechat_account wa', 'wa.wechatId = wg.ownerWechatId') ->where('wg.id', 'in', $sceneConf['wechatGroups']) ->order('wg.id', 'desc') - ->field('wg.id,wg.name as groupName,wg.ownerWechatId,wa.nickName,wa.avatar,wa.alias,wg.avatar as groupAvatar') + ->field('wg.id,wg.name,wg.chatroomId,wg.ownerWechatId,wa.nickName as ownerNickName,wa.avatar as ownerAvatar,wa.alias as ownerAlias,wg.avatar') ->select(); $sceneConf['wechatGroupsOptions'] = $groupList; }else{ @@ -138,8 +138,6 @@ class GetAddFriendPlanDetailV1Controller extends Controller } - - if (!empty($reqConf['deveiceGroups'])){ $deveiceGroupsOptions = DeviceModel::alias('d') ->field([ diff --git a/Server/application/cunkebao/controller/plan/PostCreateAddFriendPlanV1Controller.php b/Server/application/cunkebao/controller/plan/PostCreateAddFriendPlanV1Controller.php index 8b72c10a..abead6c1 100644 --- a/Server/application/cunkebao/controller/plan/PostCreateAddFriendPlanV1Controller.php +++ b/Server/application/cunkebao/controller/plan/PostCreateAddFriendPlanV1Controller.php @@ -55,7 +55,6 @@ class PostCreateAddFriendPlanV1Controller extends BaseController { try { $params = $this->request->param(); - // 验证必填字段 if (empty($params['name'])) { @@ -120,7 +119,7 @@ class PostCreateAddFriendPlanV1Controller extends BaseController 'tagConf' => json_encode($tagConf, JSON_UNESCAPED_UNICODE), 'userId' => $this->getUserInfo('id'), 'companyId' => $this->getUserInfo('companyId'), - 'status' => 1, + 'status' => !empty($params['status']) ? 1 : 0, 'apiKey' => $this->generateApiKey(), // 生成API密钥 'createTime'=> time(), 'updateTime'=> time(), @@ -263,8 +262,80 @@ class PostCreateAddFriendPlanV1Controller extends BaseController } //群获客 - if($params['sceneId'] == 7){ + if ($params['sceneId'] == 7) { + if (!empty($params['wechatGroups']) && is_array($params['wechatGroups'])) { + $rows = Db::name('wechat_group_member')->alias('gm') + ->join('wechat_account wa', 'gm.identifier = wa.wechatId') + ->where('gm.companyId', $this->getUserInfo('companyId')) + ->whereIn('gm.groupId', $params['wechatGroups']) + ->group('gm.identifier') + ->column('wa.id,wa.wechatId,wa.alias,wa.phone'); + + // 1000条为一组进行批量处理 + $batchSize = 1000; + $totalRows = count($rows); + + for ($i = 0; $i < $totalRows; $i += $batchSize) { + $batchRows = array_slice($rows, $i, $batchSize); + + if (!empty($batchRows)) { + // 1. 提取当前批次的phone + $phones = []; + foreach ($batchRows as $row) { + if (!empty($row['phone'])) { + $phone = !empty($row['phone']); + } elseif (!empty($row['alias'])) { + $phone = $row['alias']; + } else { + $phone = $row['wechatId']; + } + if (!empty($phone)) { + $phones[] = $phone; + } + } + + // 2. 批量查询已存在的phone + $existingPhones = []; + if (!empty($phones)) { + $existing = Db::name('task_customer') + ->where('task_id', $planId) + ->where('phone', 'in', $phones) + ->field('phone') + ->select(); + $existingPhones = array_column($existing, 'phone'); + } + + // 3. 过滤出新数据,批量插入 + $newData = []; + foreach ($batchRows as $row) { + if (!empty($row['phone'])) { + $phone = !empty($row['phone']); + } elseif (!empty($row['alias'])) { + $phone = $row['alias']; + } else { + $phone = $row['wechatId']; + } + if (!empty($phone) && !in_array($phone, $existingPhones)) { + $newData[] = [ + 'task_id' => $planId, + 'name' => '', + 'source' => '场景获客_' . $params['name'] ?? '', + 'phone' => $phone, + 'tags' => json_encode([], JSON_UNESCAPED_UNICODE), + 'siteTags' => json_encode([], JSON_UNESCAPED_UNICODE), + 'createTime' => time(), + ]; + } + } + + // 4. 批量插入新数据 + if (!empty($newData)) { + Db::name('task_customer')->insertAll($newData); + } + } + } + } } diff --git a/Server/application/cunkebao/controller/plan/PostUpdateAddFriendPlanV1Controller.php b/Server/application/cunkebao/controller/plan/PostUpdateAddFriendPlanV1Controller.php index 0fe1675e..7d3014d6 100644 --- a/Server/application/cunkebao/controller/plan/PostUpdateAddFriendPlanV1Controller.php +++ b/Server/application/cunkebao/controller/plan/PostUpdateAddFriendPlanV1Controller.php @@ -2,6 +2,7 @@ namespace app\cunkebao\controller\plan; +use app\cunkebao\controller\BaseController; use library\ResponseHelper; use think\Controller; use think\Db; @@ -9,7 +10,7 @@ use think\Db; /** * 更新获客计划控制器 */ -class PostUpdateAddFriendPlanV1Controller extends Controller +class PostUpdateAddFriendPlanV1Controller extends BaseController { /** * 更新计划任务 @@ -62,12 +63,6 @@ class PostUpdateAddFriendPlanV1Controller extends Controller 'endTime' => $params['endTime'] ?? '', ]; - if (isset($params['wechatGroups'])){ - $params['wechatGroups'] = $params['groupSelected']; - } - - - // 其余参数归为sceneConf $sceneConf = $params; unset( @@ -101,10 +96,12 @@ class PostUpdateAddFriendPlanV1Controller extends Controller 'reqConf' => json_encode($reqConf, JSON_UNESCAPED_UNICODE), 'msgConf' => json_encode($msgConf, JSON_UNESCAPED_UNICODE), 'tagConf' => json_encode($tagConf, JSON_UNESCAPED_UNICODE), + 'status' => !empty($params['status']) ? 1 : 0, 'updateTime' => time(), ]; + try { // 更新数据 $result = Db::name('customer_acquisition_task') @@ -242,11 +239,11 @@ class PostUpdateAddFriendPlanV1Controller extends Controller //群获客 if ($params['sceneId'] == 7) { - if (!empty($params['groupSelected']) && is_array($params['groupSelected'])) { + if (!empty($params['wechatGroups']) && is_array($params['wechatGroups'])) { $rows = Db::name('wechat_group_member')->alias('gm') ->join('wechat_account wa', 'gm.identifier = wa.wechatId') ->where('gm.companyId', $this->getUserInfo('companyId')) - ->whereIn('gm.groupId', $params['groupSelected']) + ->whereIn('gm.groupId', $params['wechatGroups']) ->group('gm.identifier') ->column('wa.id,wa.wechatId,wa.alias,wa.phone'); From 14c882ff08800f4371dca282b233fff38dd8147b Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Tue, 12 Aug 2025 15:02:30 +0800 Subject: [PATCH 03/78] =?UTF-8?q?=E8=8E=B7=E5=8F=96=E7=94=B5=E8=AF=9D?= =?UTF-8?q?=E5=BA=95=E5=B1=82=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Server/application/job/CallRecordingListJob.php | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/Server/application/job/CallRecordingListJob.php b/Server/application/job/CallRecordingListJob.php index 867afd60..95bd245c 100644 --- a/Server/application/job/CallRecordingListJob.php +++ b/Server/application/job/CallRecordingListJob.php @@ -71,20 +71,15 @@ class CallRecordingListJob 'secondMin' => 0, 'secondMax' => 99999, 'departmentIds' => '', - 'from' => '2016-01-01 00:00:00', - 'to' => '2025-08-31 00:00:00', + 'from' => date('Y-m-d') . ' 00:00:00', + 'to' => date('Y-m-d') . ' 00:00:00', 'departmentId' => '' ]; - - // 设置请求信息 - $request = request(); - $request->withGet($params); - + // 调用通话记录列表获取方法 $result = $callRecordingController->getlist($params, true); $response = json_decode($result, true); - // 判断是否成功 if ($response['code'] == 200) { $data = $response['data']; From c847eff9fc7116b2df2ad9b925b16fcb2d1656ae Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Tue, 12 Aug 2025 16:58:00 +0800 Subject: [PATCH 04/78] =?UTF-8?q?=E9=80=9A=E8=AF=9D=E8=AE=B0=E5=BD=95?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E6=B8=85=E6=B4=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/SyncWechatDataToCkbTask.php | 5 +++ .../Adapters/ChuKeBao/Adapter.php | 35 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/Server/application/command/SyncWechatDataToCkbTask.php b/Server/application/command/SyncWechatDataToCkbTask.php index 420040d9..688f4fd0 100644 --- a/Server/application/command/SyncWechatDataToCkbTask.php +++ b/Server/application/command/SyncWechatDataToCkbTask.php @@ -56,6 +56,7 @@ class SyncWechatDataToCkbTask extends Command $this->syncWechatFriendToTrafficPoolBatch($ChuKeBaoAdapter); $this->syncTrafficSourceUser($ChuKeBaoAdapter); $this->syncTrafficSourceGroup($ChuKeBaoAdapter); + $this->syncCallRecording($ChuKeBaoAdapter); $output->writeln("同步任务 sync_wechat_to_ckb 已结束"); return true; @@ -118,6 +119,10 @@ class SyncWechatDataToCkbTask extends Command { return $ChuKeBaoAdapter->syncWechatGroupCustomer(); } + protected function syncCallRecording(ChuKeBaoAdapter $ChuKeBaoAdapter) + { + return $ChuKeBaoAdapter->syncCallRecording(); + } } \ No newline at end of file diff --git a/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php b/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php index 03d7c51d..4879e277 100644 --- a/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php +++ b/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php @@ -1488,4 +1488,39 @@ class Adapter implements WeChatServiceInterface } + public function syncCallRecording() + { + $sql = "insert into ck_call_recording(`id`,`phone`,`isCallOut`,`companyId`,`callType`,`beginTime`,`endTime`,`createTime`) + SELECT + c.id id, + c.phone phone, + c.isCallOut isCallOut, + a.departmentId companyId, + c.callType callType, + c.beginTime beginTime, + c.endTime endTime, + c.callBeginTime createTime + FROM + s2_call_recording c + LEFT JOIN s2_company_account a ON c.deviceOwnerId = a.id + ORDER BY c.id DESC + LIMIT ?, ? + ON DUPLICATE KEY UPDATE + id=VALUES(id), + phone=VALUES(phone), + isCallOut=VALUES(isCallOut), + companyId=VALUES(companyId)"; + + $offset = 0; + $limit = 2000; + $usleepTime = 50000; + do { + $affected = Db::execute($sql, [$offset, $limit]); + $offset += $limit; + if ($affected > 0) { + usleep($usleepTime); + } + } while ($affected > 0); + } + } From 13577c302d2cadc4368d1a33d222a0094d75f2c8 Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Wed, 13 Aug 2025 09:43:41 +0800 Subject: [PATCH 05/78] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Server/crontab_tasks.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Server/crontab_tasks.md b/Server/crontab_tasks.md index f6852e58..8aab7567 100644 --- a/Server/crontab_tasks.md +++ b/Server/crontab_tasks.md @@ -36,6 +36,10 @@ # 微信群好友列表 */30 * * * * cd /www/wwwroot/mckb_quwanzhi_com/Server && php think groupFriends:list >> /www/wwwroot/mckb_quwanzhi_com/Server/runtime/log/group_friends_list.log 2>&1 +# 获取通话记录 +*/30 * * * * cd /www/wwwroot/mckb_quwanzhi_com/Server && php think call-recording:list >> /www/wwwroot/mckb_quwanzhi_com/Server/runtime/log/call_recording.log 2>&1 + + # 分配规则列表 0 3 * * * cd /www/wwwroot/mckb_quwanzhi_com/Server && php think allotrule:list >> /www/wwwroot/mckb_quwanzhi_com/Server/runtime/log/allot_rule_list.log 2>&1 From 46ce2f7e8d1ad297c8128c4659b3bdc6c66db2ed Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Wed, 13 Aug 2025 10:18:13 +0800 Subject: [PATCH 06/78] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cunkebao/controller/ContentLibraryController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Server/application/cunkebao/controller/ContentLibraryController.php b/Server/application/cunkebao/controller/ContentLibraryController.php index b1f6a9ab..38fdb2d1 100644 --- a/Server/application/cunkebao/controller/ContentLibraryController.php +++ b/Server/application/cunkebao/controller/ContentLibraryController.php @@ -840,7 +840,7 @@ class ContentLibraryController extends Controller $where = [ ['isDel', '=', 0], // 未删除 ['status', '=', 1], // 已开启 - ['id', '=', 61], // 已开启 +// ['id', '=', 61], // 已开启 ]; // 查询符合条件的内容库 From 5ef40510cedbe423d9f4db8e2cbe647c605f6b35 Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Wed, 13 Aug 2025 14:30:22 +0800 Subject: [PATCH 07/78] =?UTF-8?q?=E8=AE=BE=E5=A4=87=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=88=A0=E9=99=A4=E5=8F=8A=E5=A4=B4=E5=83=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/controller/DeviceController.php | 48 +++++++++++++++++++ .../device/DeleteDeviceV1Controller.php | 17 +++++-- .../device/GetDeviceListV1Controller.php | 2 +- 3 files changed, 61 insertions(+), 6 deletions(-) diff --git a/Server/application/api/controller/DeviceController.php b/Server/application/api/controller/DeviceController.php index 287ecc14..df072c2c 100644 --- a/Server/application/api/controller/DeviceController.php +++ b/Server/application/api/controller/DeviceController.php @@ -4,6 +4,7 @@ namespace app\api\controller; use app\api\model\DeviceModel; use app\api\model\DeviceGroupModel; +use think\Db; use think\facade\Request; use think\facade\Env; use Endroid\QrCode\QrCode; @@ -274,6 +275,53 @@ class DeviceController extends BaseController } } + + /** + * 删除设备 + * + * @param $deviceId + * @return false|string + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function delDevice($deviceId = '') + { + $authorization = $this->authorization; + if (empty($authorization)) { + return json_encode(['code'=>500,'msg'=>'缺少授权信息']); + } + + if (empty($deviceId)) { + return json_encode(['code'=>500,'msg'=>'删除的设备不能为空']); + } + + $device = Db::table('s2_device')->where('id', $deviceId)->find(); + if (empty($device)) { + return json_encode(['code'=>500,'msg'=>'设备不存在']); + } + + try { + // 设置请求头 + $headerData = ['client:system']; + $header = setHeader($headerData, $authorization, 'json'); + // 发送请求 + $result = requestCurl($this->baseUrl . 'api/device/del/'.$deviceId, [], 'DELETE', $header,'json'); + if (empty($result)) { + Db::table('s2_device')->where('id', $deviceId)->update([ + 'isDeleted' => 1, + 'deleteTime' => time() + ]); + return json_encode(['code'=>200,'msg'=>'删除成功']); + }else{ + return json_encode(['code'=>200,'msg'=>'删除失败']); + } + } catch (\Exception $e) { + return json_encode(['code'=>500,'msg'=>'获取设备分组列表失败:' . $e->getMessage()]); + } + } + + /************************ 设备分组相关接口 ************************/ /** diff --git a/Server/application/cunkebao/controller/device/DeleteDeviceV1Controller.php b/Server/application/cunkebao/controller/device/DeleteDeviceV1Controller.php index a2b149ad..cb1b4a22 100644 --- a/Server/application/cunkebao/controller/device/DeleteDeviceV1Controller.php +++ b/Server/application/cunkebao/controller/device/DeleteDeviceV1Controller.php @@ -9,6 +9,7 @@ use app\common\model\User as UserModel; use app\cunkebao\controller\BaseController; use library\ResponseHelper; use think\Db; +use app\api\controller\DeviceController as apiDevice; /** * 设备管理控制器 @@ -83,11 +84,17 @@ class DeleteDeviceV1Controller extends BaseController */ protected function deleteCkbAbout(int $id): self { - $this->deleteDevice($id); - $this->deleteDeviceConf($id); - $this->deleteDeviceUser($id); - - return $this; + $apiDevice = new ApiDevice(); + $res = $apiDevice->delDevice($id); + $res = json_decode($res, true); + if ($res['code'] == 200){ + $this->deleteDevice($id); + $this->deleteDeviceConf($id); + $this->deleteDeviceUser($id); + return $this; + }else{ + return false; + } } /** diff --git a/Server/application/cunkebao/controller/device/GetDeviceListV1Controller.php b/Server/application/cunkebao/controller/device/GetDeviceListV1Controller.php index 33410d13..78a21c1a 100644 --- a/Server/application/cunkebao/controller/device/GetDeviceListV1Controller.php +++ b/Server/application/cunkebao/controller/device/GetDeviceListV1Controller.php @@ -75,7 +75,7 @@ class GetDeviceListV1Controller extends BaseController ->field([ 'd.id', 'd.imei', 'd.memo', 'd.alive', 'l.wechatId', - 'a.nickname', 'a.alias', '0 totalFriend' + 'a.nickname', 'a.alias', 'a.avatar', '0 totalFriend' ]) ->leftJoin('device_wechat_login l', 'd.id = l.deviceId and l.alive =' . DeviceWechatLoginModel::ALIVE_WECHAT_ACTIVE . ' and l.companyId = d.companyId') ->leftJoin('wechat_account a', 'l.wechatId = a.wechatId') From f5dd5e8191d1d5a2aafbe3e665bd59792f24862c Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Wed, 13 Aug 2025 17:02:36 +0800 Subject: [PATCH 08/78] =?UTF-8?q?=E7=94=B5=E8=AF=9D=E8=8E=B7=E5=AE=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Server/application/common/TaskServer.php | 22 +- .../PostCreateAddFriendPlanV1Controller.php | 257 ++++++++---------- .../PostUpdateAddFriendPlanV1Controller.php | 184 +++++-------- ...PotentialListWithInCompanyV1Controller.php | 2 +- .../Adapters/ChuKeBao/Adapter.php | 125 +++++---- 5 files changed, 262 insertions(+), 328 deletions(-) diff --git a/Server/application/common/TaskServer.php b/Server/application/common/TaskServer.php index 5a6d2aa4..5af9303e 100644 --- a/Server/application/common/TaskServer.php +++ b/Server/application/common/TaskServer.php @@ -16,8 +16,8 @@ class TaskServer extends Server protected $socket = 'text://0.0.0.0:2980'; protected $option = [ - 'count' => self::PROCESS_COUNT, - 'name' => 'ckb_task_server' + 'count' => self::PROCESS_COUNT, + 'name' => 'ckb_task_server' ]; /** @@ -31,11 +31,17 @@ class TaskServer extends Server Log::record("error $code $msg"); } - public function onMessage($connection, $data) {} + public function onMessage($connection, $data) + { + } - public function onClose($connection) {} + public function onClose($connection) + { + } - public function onConnect($connection) {} + public function onConnect($connection) + { + } public function onWorkerStart($worker) { @@ -52,7 +58,7 @@ class TaskServer extends Server // 在一个进程里处理获客任务新是数据 if ($current_worker_id == 4) { - Timer::add(60, function () use($adapter) { + Timer::add(60, function () use ($adapter) { $adapter->handleCustomerTaskNewUser(); }); } @@ -60,7 +66,7 @@ class TaskServer extends Server // 在一个进程里处理获客任务添加后的相关逻辑 if ($current_worker_id == 3) { - Timer::add(60, function () use($adapter) { + Timer::add(60, function () use ($adapter) { $adapter->handleCustomerTaskWithStatusIsCreated(); }); } @@ -74,6 +80,6 @@ class TaskServer extends Server // 更多其他后台任务 // ...... - + } } diff --git a/Server/application/cunkebao/controller/plan/PostCreateAddFriendPlanV1Controller.php b/Server/application/cunkebao/controller/plan/PostCreateAddFriendPlanV1Controller.php index abead6c1..63cddf97 100644 --- a/Server/application/cunkebao/controller/plan/PostCreateAddFriendPlanV1Controller.php +++ b/Server/application/cunkebao/controller/plan/PostCreateAddFriendPlanV1Controller.php @@ -16,7 +16,7 @@ class PostCreateAddFriendPlanV1Controller extends BaseController /** * 生成唯一API密钥 - * + * * @return string */ public function generateApiKey() @@ -24,7 +24,7 @@ class PostCreateAddFriendPlanV1Controller extends BaseController // 生成5组随机字符串,每组5个字符 $chars = 'abcdefghijklmnopqrstuvwxyz0123456789'; $apiKey = ''; - + for ($i = 0; $i < 5; $i++) { $segment = ''; for ($j = 0; $j < 5; $j++) { @@ -32,17 +32,17 @@ class PostCreateAddFriendPlanV1Controller extends BaseController } $apiKey .= ($i > 0 ? '-' : '') . $segment; } - + // 检查是否已存在 $exists = Db::name('customer_acquisition_task') ->where('apiKey', $apiKey) ->find(); - + if ($exists) { // 如果已存在,递归重新生成 return $this->generateApiKey(); } - + return $apiKey; } @@ -60,29 +60,29 @@ class PostCreateAddFriendPlanV1Controller extends BaseController if (empty($params['name'])) { return ResponseHelper::error('计划名称不能为空', 400); } - + if (empty($params['sceneId'])) { return ResponseHelper::error('场景ID不能为空', 400); } - + if (empty($params['deveiceGroups'])) { return ResponseHelper::error('请选择设备', 400); } - + // 归类参数 $msgConf = isset($params['messagePlans']) ? $params['messagePlans'] : []; $tagConf = [ 'scenarioTags' => $params['scenarioTags'] ?? [], - 'customTags' => $params['customTags'] ?? [], + 'customTags' => $params['customTags'] ?? [], ]; $reqConf = [ - 'device' => $params['deveiceGroups'] ?? [], - 'remarkType' => $params['remarkType'] ?? '', - 'greeting' => $params['greeting'] ?? '', + 'device' => $params['deveiceGroups'] ?? [], + 'remarkType' => $params['remarkType'] ?? '', + 'greeting' => $params['greeting'] ?? '', 'addFriendInterval' => $params['addFriendInterval'] ?? '', - 'startTime' => $params['startTime'] ?? '', - 'endTime' => $params['endTime'] ?? '', + 'startTime' => $params['startTime'] ?? '', + 'endTime' => $params['endTime'] ?? '', ]; // 其余参数归为sceneConf $sceneConf = $params; @@ -111,18 +111,18 @@ class PostCreateAddFriendPlanV1Controller extends BaseController // 构建数据 $data = [ - 'name' => $params['name'], - 'sceneId' => $params['sceneId'], + 'name' => $params['name'], + 'sceneId' => $params['sceneId'], 'sceneConf' => json_encode($sceneConf, JSON_UNESCAPED_UNICODE), - 'reqConf' => json_encode($reqConf, JSON_UNESCAPED_UNICODE), - 'msgConf' => json_encode($msgConf, JSON_UNESCAPED_UNICODE), - 'tagConf' => json_encode($tagConf, JSON_UNESCAPED_UNICODE), - 'userId' => $this->getUserInfo('id'), + 'reqConf' => json_encode($reqConf, JSON_UNESCAPED_UNICODE), + 'msgConf' => json_encode($msgConf, JSON_UNESCAPED_UNICODE), + 'tagConf' => json_encode($tagConf, JSON_UNESCAPED_UNICODE), + 'userId' => $this->getUserInfo('id'), 'companyId' => $this->getUserInfo('companyId'), - 'status' => !empty($params['status']) ? 1 : 0, - 'apiKey' => $this->generateApiKey(), // 生成API密钥 - 'createTime'=> time(), - 'updateTime'=> time(), + 'status' => !empty($params['status']) ? 1 : 0, + 'apiKey' => $this->generateApiKey(), // 生成API密钥 + 'createTime' => time(), + 'updateTime' => time(), ]; @@ -130,15 +130,15 @@ class PostCreateAddFriendPlanV1Controller extends BaseController Db::startTrans(); // 插入数据 $planId = Db::name('customer_acquisition_task')->insertGetId($data); - + if (!$planId) { throw new \Exception('添加计划失败'); } //订单 - if($params['sceneId'] == 2){ - if(!empty($params['orderTableFile'])){ + if ($params['sceneId'] == 2) { + if (!empty($params['orderTableFile'])) { // 先下载到本地临时文件,再分析,最后删除 $originPath = $params['orderTableFile']; $tmpFile = tempnam(sys_get_temp_dir(), 'order_'); @@ -171,12 +171,12 @@ class PostCreateAddFriendPlanV1Controller extends BaseController foreach ($data as $cols) { $rows[] = [ - 'name' => isset($cols[0]) ? trim($cols[0]) : '', - 'phone' => isset($cols[1]) ? trim($cols[1]) : '', - 'wechat' => isset($cols[2]) ? trim($cols[2]) : '', - 'source' => isset($cols[3]) ? trim($cols[3]) : '', + 'name' => isset($cols[0]) ? trim($cols[0]) : '', + 'phone' => isset($cols[1]) ? trim($cols[1]) : '', + 'wechatId' => isset($cols[2]) ? trim($cols[2]) : '', + 'source' => isset($cols[3]) ? trim($cols[3]) : '', 'orderAmount' => isset($cols[4]) ? trim($cols[4]) : '', - 'orderDate' => isset($cols[5]) ? trim($cols[5]) : '', + 'orderDate' => isset($cols[5]) ? trim($cols[5]) : '', ]; } } elseif ($ext === 'csv') { @@ -189,12 +189,12 @@ class PostCreateAddFriendPlanV1Controller extends BaseController $cols = str_getcsv($line); if (count($cols) >= 6) { $rows[] = [ - 'name' => isset($cols[0]) ? trim($cols[0]) : '', - 'phone' => isset($cols[1]) ? trim($cols[1]) : '', - 'wechat' => isset($cols[2]) ? trim($cols[2]) : '', - 'source' => isset($cols[3]) ? trim($cols[3]) : '', + 'name' => isset($cols[0]) ? trim($cols[0]) : '', + 'phone' => isset($cols[1]) ? trim($cols[1]) : '', + 'wechatId' => isset($cols[2]) ? trim($cols[2]) : '', + 'source' => isset($cols[3]) ? trim($cols[3]) : '', 'orderAmount' => isset($cols[4]) ? trim($cols[4]) : '', - 'orderDate' => isset($cols[5]) ? trim($cols[5]) : '', + 'orderDate' => isset($cols[5]) ? trim($cols[5]) : '', ]; } } @@ -205,62 +205,19 @@ class PostCreateAddFriendPlanV1Controller extends BaseController } // 删除临时文件 unlink($tmpFile); - - // 1000条为一组进行批量处理 - $batchSize = 1000; - $totalRows = count($rows); - - for ($i = 0; $i < $totalRows; $i += $batchSize) { - $batchRows = array_slice($rows, $i, $batchSize); - - if (!empty($batchRows)) { - // 1. 提取当前批次的phone - $phones = []; - foreach ($batchRows as $row) { - $phone = !empty($row['phone']) ? $row['phone'] : $row['wechat']; - if (!empty($phone)) { - $phones[] = $phone; - } - } - - // 2. 批量查询已存在的phone - $existingPhones = []; - if (!empty($phones)) { - $existing = Db::name('task_customer') - ->where('task_id', $planId) - ->where('phone', 'in', $phones) - ->field('phone') - ->select(); - $existingPhones = array_column($existing, 'phone'); - } - - // 3. 过滤出新数据,批量插入 - $newData = []; - foreach ($batchRows as $row) { - $phone = !empty($row['phone']) ? $row['phone'] : $row['wechat']; - if (!empty($phone) && !in_array($phone, $existingPhones)) { - $newData[] = [ - 'task_id' => $planId, - 'name' => $row['name'] ?? '', - 'source' => $row['source'] ?? '', - 'phone' => $phone, - 'tags' => json_encode([], JSON_UNESCAPED_UNICODE), - 'siteTags' => json_encode([], JSON_UNESCAPED_UNICODE), - 'createTime' => time(), - ]; - } - } - - // 4. 批量插入新数据 - if (!empty($newData)) { - Db::name('task_customer')->insertAll($newData); - } - } - } - } } + //电话获客 + if ($params['sceneId'] == 5) { + $rows = Db::name('call_recording') + ->where('companyId', $this->getUserInfo('companyId')) + ->group('phone') + ->field('id,phone') + ->select(); + } + + //群获客 if ($params['sceneId'] == 7) { if (!empty($params['wechatGroups']) && is_array($params['wechatGroups'])) { @@ -270,69 +227,71 @@ class PostCreateAddFriendPlanV1Controller extends BaseController ->whereIn('gm.groupId', $params['wechatGroups']) ->group('gm.identifier') ->column('wa.id,wa.wechatId,wa.alias,wa.phone'); + } + } - // 1000条为一组进行批量处理 - $batchSize = 1000; - $totalRows = count($rows); + if (in_array($params['sceneId'], [2, 5, 7]) && !empty($rows) && is_array($rows)) { + // 1000条为一组进行批量处理 + $batchSize = 1000; + $totalRows = count($rows); - for ($i = 0; $i < $totalRows; $i += $batchSize) { - $batchRows = array_slice($rows, $i, $batchSize); + for ($i = 0; $i < $totalRows; $i += $batchSize) { + $batchRows = array_slice($rows, $i, $batchSize); - if (!empty($batchRows)) { - // 1. 提取当前批次的phone - $phones = []; - foreach ($batchRows as $row) { - if (!empty($row['phone'])) { - $phone = !empty($row['phone']); - } elseif (!empty($row['alias'])) { - $phone = $row['alias']; - } else { - $phone = $row['wechatId']; - } - if (!empty($phone)) { - $phones[] = $phone; - } + if (!empty($batchRows)) { + // 1. 提取当前批次的phone + $phones = []; + foreach ($batchRows as $row) { + if (!empty($row['phone'])) { + $phone = $row['phone']; + } elseif (!empty($row['alias'])) { + $phone = $row['alias']; + } else { + $phone = $row['wechatId']; } - - // 2. 批量查询已存在的phone - $existingPhones = []; - if (!empty($phones)) { - $existing = Db::name('task_customer') - ->where('task_id', $planId) - ->where('phone', 'in', $phones) - ->field('phone') - ->select(); - $existingPhones = array_column($existing, 'phone'); + if (!empty($phone)) { + $phones[] = $phone; } + } - // 3. 过滤出新数据,批量插入 - $newData = []; - foreach ($batchRows as $row) { - if (!empty($row['phone'])) { - $phone = !empty($row['phone']); - } elseif (!empty($row['alias'])) { - $phone = $row['alias']; - } else { - $phone = $row['wechatId']; - } - if (!empty($phone) && !in_array($phone, $existingPhones)) { - $newData[] = [ - 'task_id' => $planId, - 'name' => '', - 'source' => '场景获客_' . $params['name'] ?? '', - 'phone' => $phone, - 'tags' => json_encode([], JSON_UNESCAPED_UNICODE), - 'siteTags' => json_encode([], JSON_UNESCAPED_UNICODE), - 'createTime' => time(), - ]; - } - } + // 2. 批量查询已存在的phone + $existingPhones = []; + if (!empty($phones)) { + $existing = Db::name('task_customer') + ->where('task_id', $planId) + ->where('phone', 'in', $phones) + ->field('phone') + ->select(); + $existingPhones = array_column($existing, 'phone'); + } - // 4. 批量插入新数据 - if (!empty($newData)) { - Db::name('task_customer')->insertAll($newData); + // 3. 过滤出新数据,批量插入 + $newData = []; + foreach ($batchRows as $row) { + if (!empty($row['phone'])) { + $phone = $row['phone']; + } elseif (!empty($row['alias'])) { + $phone = $row['alias']; + } else { + $phone = $row['wechatId']; } + if (!empty($phone) && !in_array($phone, $existingPhones)) { + $newData[] = [ + 'task_id' => $planId, + 'name' => '', + 'source' => '场景获客_' . $params['name'] ?? '', + 'phone' => $phone, + 'tags' => json_encode([], JSON_UNESCAPED_UNICODE), + 'siteTags' => json_encode([], JSON_UNESCAPED_UNICODE), + 'createTime' => time(), + ]; + } + } + + // 4. 批量插入新数据 + if (!empty($newData)) { + Db::name('task_customer')->insertAll($newData); } } } @@ -340,21 +299,21 @@ class PostCreateAddFriendPlanV1Controller extends BaseController Db::commit(); - + return ResponseHelper::success(['planId' => $planId], '添加计划任务成功'); - + } catch (\Exception $e) { // 回滚事务 Db::rollback(); throw $e; } - + } catch (\Exception $e) { return ResponseHelper::error('系统错误: ' . $e->getMessage(), 500); } } - /** + /** * 验证JSON格式是否正确 * * @param string $string @@ -365,7 +324,7 @@ class PostCreateAddFriendPlanV1Controller extends BaseController if (empty($string)) { return true; } - + json_decode($string); return (json_last_error() == JSON_ERROR_NONE); } diff --git a/Server/application/cunkebao/controller/plan/PostUpdateAddFriendPlanV1Controller.php b/Server/application/cunkebao/controller/plan/PostUpdateAddFriendPlanV1Controller.php index 7d3014d6..1e70104a 100644 --- a/Server/application/cunkebao/controller/plan/PostUpdateAddFriendPlanV1Controller.php +++ b/Server/application/cunkebao/controller/plan/PostUpdateAddFriendPlanV1Controller.php @@ -10,7 +10,7 @@ use think\Db; /** * 更新获客计划控制器 */ -class PostUpdateAddFriendPlanV1Controller extends BaseController +class PostUpdateAddFriendPlanV1Controller extends BaseController { /** * 更新计划任务 @@ -96,12 +96,11 @@ class PostUpdateAddFriendPlanV1Controller extends BaseController 'reqConf' => json_encode($reqConf, JSON_UNESCAPED_UNICODE), 'msgConf' => json_encode($msgConf, JSON_UNESCAPED_UNICODE), 'tagConf' => json_encode($tagConf, JSON_UNESCAPED_UNICODE), - 'status' => !empty($params['status']) ? 1 : 0, + 'status' => !empty($params['status']) ? 1 : 0, 'updateTime' => time(), ]; - try { // 更新数据 $result = Db::name('customer_acquisition_task') @@ -149,7 +148,7 @@ class PostUpdateAddFriendPlanV1Controller extends BaseController $rows[] = [ 'name' => isset($cols[0]) ? trim($cols[0]) : '', 'phone' => isset($cols[1]) ? trim($cols[1]) : '', - 'wechat' => isset($cols[2]) ? trim($cols[2]) : '', + 'wechatId' => isset($cols[2]) ? trim($cols[2]) : '', 'source' => isset($cols[3]) ? trim($cols[3]) : '', 'orderAmount' => isset($cols[4]) ? trim($cols[4]) : '', 'orderDate' => isset($cols[5]) ? trim($cols[5]) : '', @@ -167,7 +166,7 @@ class PostUpdateAddFriendPlanV1Controller extends BaseController $rows[] = [ 'name' => isset($cols[0]) ? trim($cols[0]) : '', 'phone' => isset($cols[1]) ? trim($cols[1]) : '', - 'wechat' => isset($cols[2]) ? trim($cols[2]) : '', + 'wechatId' => isset($cols[2]) ? trim($cols[2]) : '', 'source' => isset($cols[3]) ? trim($cols[3]) : '', 'orderAmount' => isset($cols[4]) ? trim($cols[4]) : '', 'orderDate' => isset($cols[5]) ? trim($cols[5]) : '', @@ -181,62 +180,19 @@ class PostUpdateAddFriendPlanV1Controller extends BaseController } // 删除临时文件 unlink($tmpFile); - - // 1000条为一组进行批量处理 - $batchSize = 1000; - $totalRows = count($rows); - - for ($i = 0; $i < $totalRows; $i += $batchSize) { - $batchRows = array_slice($rows, $i, $batchSize); - - if (!empty($batchRows)) { - // 1. 提取当前批次的phone - $phones = []; - foreach ($batchRows as $row) { - $phone = !empty($row['phone']) ? $row['phone'] : $row['wechat']; - if (!empty($phone)) { - $phones[] = $phone; - } - } - - // 2. 批量查询已存在的phone - $existingPhones = []; - if (!empty($phones)) { - $existing = Db::name('task_customer') - ->where('task_id', $params['planId']) - ->where('phone', 'in', $phones) - ->field('phone') - ->select(); - $existingPhones = array_column($existing, 'phone'); - } - - // 3. 过滤出新数据,批量插入 - $newData = []; - foreach ($batchRows as $row) { - $phone = !empty($row['phone']) ? $row['phone'] : $row['wechat']; - if (!empty($phone) && !in_array($phone, $existingPhones)) { - $newData[] = [ - 'task_id' => $params['planId'], - 'name' => $row['name'] ?? '', - 'source' => $row['source'] ?? '', - 'phone' => $phone, - 'tags' => json_encode([], JSON_UNESCAPED_UNICODE), - 'siteTags' => json_encode([], JSON_UNESCAPED_UNICODE), - 'createTime' => time(), - ]; - } - } - - // 4. 批量插入新数据 - if (!empty($newData)) { - Db::name('task_customer')->insertAll($newData); - } - } - } - } } + + //电话获客 + if ($params['sceneId'] == 5) { + $rows = Db::name('call_recording') + ->where('companyId', $this->getUserInfo('companyId')) + ->group('phone') + ->field('id,phone') + ->select(); + } + //群获客 if ($params['sceneId'] == 7) { if (!empty($params['wechatGroups']) && is_array($params['wechatGroups'])) { @@ -246,74 +202,76 @@ class PostUpdateAddFriendPlanV1Controller extends BaseController ->whereIn('gm.groupId', $params['wechatGroups']) ->group('gm.identifier') ->column('wa.id,wa.wechatId,wa.alias,wa.phone'); + } + } - // 1000条为一组进行批量处理 - $batchSize = 1000; - $totalRows = count($rows); + if (in_array($params['sceneId'], [2, 5, 7]) && !empty($rows) && is_array($rows)) { + // 1000条为一组进行批量处理 + $batchSize = 1000; + $totalRows = count($rows); - for ($i = 0; $i < $totalRows; $i += $batchSize) { - $batchRows = array_slice($rows, $i, $batchSize); - - if (!empty($batchRows)) { - // 1. 提取当前批次的phone - $phones = []; - foreach ($batchRows as $row) { - if (!empty($row['phone'])) { - $phone = !empty($row['phone']); - } elseif (!empty($row['alias'])) { - $phone = $row['alias']; - } else { - $phone = $row['wechatId']; - } - if (!empty($phone)) { - $phones[] = $phone; - } + for ($i = 0; $i < $totalRows; $i += $batchSize) { + $batchRows = array_slice($rows, $i, $batchSize); + if (!empty($batchRows)) { + // 1. 提取当前批次的phone + // 1. 提取当前批次的phone + $phones = []; + foreach ($batchRows as $row) { + if (!empty($row['phone'])) { + $phone = $row['phone']; + } elseif (!empty($row['alias'])) { + $phone = $row['alias']; + } else { + $phone = $row['wechatId']; } - - // 2. 批量查询已存在的phone - $existingPhones = []; - if (!empty($phones)) { - $existing = Db::name('task_customer') - ->where('task_id', $params['planId']) - ->where('phone', 'in', $phones) - ->field('phone') - ->select(); - $existingPhones = array_column($existing, 'phone'); + if (!empty($phone)) { + $phones[] = $phone; } + } + // 2. 批量查询已存在的phone + $existingPhones = []; + if (!empty($phones)) { + $existing = Db::name('task_customer') + ->where('task_id', $params['planId']) + ->where('phone', 'in', $phones) + ->field('phone') + ->select(); + $existingPhones = array_column($existing, 'phone'); + } - // 3. 过滤出新数据,批量插入 - $newData = []; - foreach ($batchRows as $row) { - if (!empty($row['phone'])) { - $phone = !empty($row['phone']); - } elseif (!empty($row['alias'])) { - $phone = $row['alias']; - } else { - $phone = $row['wechatId']; - } - if (!empty($phone) && !in_array($phone, $existingPhones)) { - $newData[] = [ - 'task_id' => $params['planId'], - 'name' => '', - 'source' => '场景获客_' . $params['name'] ?? '', - 'phone' => $phone, - 'tags' => json_encode([], JSON_UNESCAPED_UNICODE), - 'siteTags' => json_encode([], JSON_UNESCAPED_UNICODE), - 'createTime' => time(), - ]; - } + // 3. 过滤出新数据,批量插入 + $newData = []; + foreach ($batchRows as $row) { + if (!empty($row['phone'])) { + $phone = $row['phone']; + } elseif (!empty($row['alias'])) { + $phone = $row['alias']; + } else { + $phone = $row['wechatId']; } + if (!empty($phone) && !in_array($phone, $existingPhones)) { + $newData[] = [ + 'task_id' => $params['planId'], + 'name' => !empty($row['name']) ? $row['name'] : '', + 'source' => '场景获客_' . $params['name'] ?? '', + 'phone' => $phone, + 'tags' => json_encode([], JSON_UNESCAPED_UNICODE), + 'siteTags' => json_encode([], JSON_UNESCAPED_UNICODE), + 'createTime' => time(), + ]; + } + } - // 4. 批量插入新数据 - if (!empty($newData)) { - Db::name('task_customer')->insertAll($newData); - } + // 4. 批量插入新数据 + if (!empty($newData)) { + Db::name('task_customer')->insertAll($newData); } } } } + return ResponseHelper::success(['planId' => $params['planId']], '更新计划任务成功'); } catch (\Exception $e) { diff --git a/Server/application/cunkebao/controller/traffic/GetPotentialListWithInCompanyV1Controller.php b/Server/application/cunkebao/controller/traffic/GetPotentialListWithInCompanyV1Controller.php index ec35f503..4085727d 100644 --- a/Server/application/cunkebao/controller/traffic/GetPotentialListWithInCompanyV1Controller.php +++ b/Server/application/cunkebao/controller/traffic/GetPotentialListWithInCompanyV1Controller.php @@ -43,7 +43,7 @@ class GetPotentialListWithInCompanyV1Controller extends BaseController } - if ($device = $this->request->param('device')) { + if ($device = $this->request->param('deviceId')) { $where['d.deviceId'] = $device; } diff --git a/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php b/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php index 4879e277..1998a070 100644 --- a/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php +++ b/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php @@ -339,7 +339,7 @@ class Adapter implements WeChatServiceInterface { $task = Db::name('customer_acquisition_task') ->where(['status' => 1, 'deleteTime' => 0]) - ->whereIn('sceneId', [7]) + ->whereIn('sceneId', [5, 7]) ->order('id desc') ->select(); @@ -349,7 +349,17 @@ class Adapter implements WeChatServiceInterface foreach ($task as $item) { $sceneConf = json_decode($item['sceneConf'], true); - //群获客 + //电话 + if ($item['sceneId'] == 5) { + $rows = Db::name('call_recording') + ->where('companyId', $item['companyId']) + ->group('phone') + ->field('id,phone') + ->order('id asc') + ->limit(0, 100) + ->select(); + } + if ($item['sceneId'] == 7) { if (!empty($sceneConf['groupSelected']) && is_array($sceneConf['groupSelected'])) { $rows = Db::name('wechat_group_member')->alias('gm') @@ -358,76 +368,77 @@ class Adapter implements WeChatServiceInterface ->whereIn('gm.groupId', $sceneConf['groupSelected']) ->group('gm.identifier') ->column('wa.id,wa.wechatId,wa.alias,wa.phone'); + } + } - // 1000条为一组进行批量处理 - $batchSize = 1000; - $totalRows = count($rows); + if (in_array($item['sceneId'], [5, 7]) && !empty($rows) && is_array($rows)) { + // 1000条为一组进行批量处理 + $batchSize = 1000; + $totalRows = count($rows); - for ($i = 0; $i < $totalRows; $i += $batchSize) { - $batchRows = array_slice($rows, $i, $batchSize); + for ($i = 0; $i < $totalRows; $i += $batchSize) { + $batchRows = array_slice($rows, $i, $batchSize); - if (!empty($batchRows)) { - // 1. 提取当前批次的phone - $phones = []; - foreach ($batchRows as $row) { - if (!empty($row['phone'])) { - $phone = !empty($row['phone']); - } elseif (!empty($row['alias'])) { - $phone = $row['alias']; - } else { - $phone = $row['wechatId']; - } - if (!empty($phone)) { - $phones[] = $phone; - } + if (!empty($batchRows)) { + // 1. 提取当前批次的phone + $phones = []; + foreach ($batchRows as $row) { + if (!empty($row['phone'])) { + $phone = !empty($row['phone']); + } elseif (!empty($row['alias'])) { + $phone = $row['alias']; + } else { + $phone = $row['wechatId']; } - - // 2. 批量查询已存在的phone - $existingPhones = []; - if (!empty($phones)) { - $existing = Db::name('task_customer') - ->where('task_id', $item['id']) - ->where('phone', 'in', $phones) - ->field('phone') - ->select(); - $existingPhones = array_column($existing, 'phone'); + if (!empty($phone)) { + $phones[] = $phone; } + } - // 3. 过滤出新数据,批量插入 - $newData = []; - foreach ($batchRows as $row) { - if (!empty($row['phone'])) { - $phone = !empty($row['phone']); - } elseif (!empty($row['alias'])) { - $phone = $row['alias']; - } else { - $phone = $row['wechatId']; - } - if (!empty($phone) && !in_array($phone, $existingPhones)) { - $newData[] = [ - 'task_id' => $item['id'], - 'name' => '', - 'source' => '场景获客_' . $item['name'], - 'phone' => $phone, - 'tags' => json_encode([], JSON_UNESCAPED_UNICODE), - 'siteTags' => json_encode([], JSON_UNESCAPED_UNICODE), - 'createTime' => time(), - ]; - } - } + // 2. 批量查询已存在的phone + $existingPhones = []; + if (!empty($phones)) { + $existing = Db::name('task_customer') + ->where('task_id', $item['id']) + ->where('phone', 'in', $phones) + ->field('phone') + ->select(); + $existingPhones = array_column($existing, 'phone'); + } - // 4. 批量插入新数据 - if (!empty($newData)) { - Db::name('task_customer')->insertAll($newData); + // 3. 过滤出新数据,批量插入 + $newData = []; + foreach ($batchRows as $row) { + if (!empty($row['phone'])) { + $phone = !empty($row['phone']); + } elseif (!empty($row['alias'])) { + $phone = $row['alias']; + } else { + $phone = $row['wechatId']; } + if (!empty($phone) && !in_array($phone, $existingPhones)) { + $newData[] = [ + 'task_id' => $item['id'], + 'name' => '', + 'source' => '场景获客_' . $item['name'], + 'phone' => $phone, + 'tags' => json_encode([], JSON_UNESCAPED_UNICODE), + 'siteTags' => json_encode([], JSON_UNESCAPED_UNICODE), + 'createTime' => time(), + ]; + } + } + + // 4. 批量插入新数据 + if (!empty($newData)) { + Db::name('task_customer')->insertAll($newData); } } } } - exit_data($sceneConf); } } From e53f31487ec6f3f5170cd1f2cb59ee956faa6ce3 Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Wed, 13 Aug 2025 17:03:25 +0800 Subject: [PATCH 09/78] =?UTF-8?q?=E9=97=A8=E5=BA=97=E7=AB=AF=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Store_vue/api/config/index.js | 4 ++-- Store_vue/components/SideMenu.vue | 6 +++--- Store_vue/components/SupplyChainPurchase.vue | 4 ++-- Store_vue/components/SupplyItemDetail.vue | 4 ++-- Store_vue/pages/chat/index.vue | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Store_vue/api/config/index.js b/Store_vue/api/config/index.js index 1b2a84bf..29c6eba6 100644 --- a/Store_vue/api/config/index.js +++ b/Store_vue/api/config/index.js @@ -1,8 +1,8 @@ // API配置文件 // 基础配置 -export const BASE_URL = 'http://yi.cn' -//export const BASE_URL = 'https://ckbapi.quwanzhi.com' +//export const BASE_URL = 'http://yishi.com' +export const BASE_URL = 'https://ckbapi.quwanzhi.com' // 获取请求头 const getHeaders = (options = {}) => { diff --git a/Store_vue/components/SideMenu.vue b/Store_vue/components/SideMenu.vue index 58be6fb0..1ca22393 100644 --- a/Store_vue/components/SideMenu.vue +++ b/Store_vue/components/SideMenu.vue @@ -3,10 +3,10 @@ - AI赋能 - + AI助手 + diff --git a/Store_vue/components/SupplyChainPurchase.vue b/Store_vue/components/SupplyChainPurchase.vue index 83fe4e00..cd228c10 100644 --- a/Store_vue/components/SupplyChainPurchase.vue +++ b/Store_vue/components/SupplyChainPurchase.vue @@ -4,11 +4,11 @@ - + 供应链采购 - + diff --git a/Store_vue/components/SupplyItemDetail.vue b/Store_vue/components/SupplyItemDetail.vue index d82ac268..9b6099e9 100644 --- a/Store_vue/components/SupplyItemDetail.vue +++ b/Store_vue/components/SupplyItemDetail.vue @@ -4,11 +4,11 @@ - + {{detailData.name || packageData.name}} - + diff --git a/Store_vue/pages/chat/index.vue b/Store_vue/pages/chat/index.vue index 7f131361..1635a2e6 100644 --- a/Store_vue/pages/chat/index.vue +++ b/Store_vue/pages/chat/index.vue @@ -84,7 +84,7 @@ - 艺施内容库 + 内容库 @@ -157,7 +157,7 @@ contentLibEnabled: true, autoCustomerEnabled: false, scrollTop: 0, - menuVisible: false, + menuVisible: true, isRecording: false, messages: [], pageSize: 20, From 25ae2458f0475e34155d49dcfa787062e83d0a59 Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Fri, 15 Aug 2025 15:37:00 +0800 Subject: [PATCH 10/78] =?UTF-8?q?=E6=B5=81=E9=87=8F=E6=B1=A0=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E5=8F=8A=E5=9C=BA=E6=99=AF=E8=8E=B7=E5=AE=A2=E7=BB=9F?= =?UTF-8?q?=E8=AE=A1=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Server/application/cunkebao/config/route.php | 17 +- .../cunkebao/controller/StatsController.php | 154 +++++++++++- .../controller/WorkbenchController.php | 10 +- .../GetAddFriendPlanDetailV1Controller.php | 8 +- ...PotentialListWithInCompanyV1Controller.php | 227 +++++++++++++----- .../Adapters/ChuKeBao/Adapter.php | 5 +- 6 files changed, 351 insertions(+), 70 deletions(-) diff --git a/Server/application/cunkebao/config/route.php b/Server/application/cunkebao/config/route.php index 2a45e9f6..6e0ebd8f 100644 --- a/Server/application/cunkebao/config/route.php +++ b/Server/application/cunkebao/config/route.php @@ -67,6 +67,8 @@ Route::group('v1/', function () { Route::get('getUserJourney', 'app\cunkebao\controller\traffic\GetPotentialListWithInCompanyV1Controller@getUserJourney'); Route::get('getUserTags', 'app\cunkebao\controller\traffic\GetPotentialListWithInCompanyV1Controller@getUserTags'); Route::get('getUserInfo', 'app\cunkebao\controller\traffic\GetPotentialListWithInCompanyV1Controller@getUser'); + Route::get('getPackage', 'app\cunkebao\controller\traffic\GetPotentialListWithInCompanyV1Controller@getPackage'); + Route::post('addPackage', 'app\cunkebao\controller\traffic\GetPotentialListWithInCompanyV1Controller@addPackage'); @@ -128,13 +130,14 @@ Route::group('v1/', function () { }); - //数据统计相关 - Route::group('dashboard',function (){ - Route::get('', 'app\cunkebao\controller\StatsController@baseInfoStats'); - Route::get('plan-stats', 'app\cunkebao\controller\StatsController@planStats'); - Route::get('sevenDay-stats', 'app\cunkebao\controller\StatsController@customerAcquisitionStats7Days'); - Route::get('today-stats', 'app\cunkebao\controller\StatsController@todayStats'); - }); + //数据统计相关 + Route::group('dashboard',function (){ + Route::get('', 'app\cunkebao\controller\StatsController@baseInfoStats'); + Route::get('plan-stats', 'app\cunkebao\controller\StatsController@planStats'); + Route::get('sevenDay-stats', 'app\cunkebao\controller\StatsController@customerAcquisitionStats7Days'); + Route::get('today-stats', 'app\cunkebao\controller\StatsController@todayStats'); + Route::get('friendRequestTaskStats', 'app\cunkebao\controller\StatsController@getFriendRequestTaskStats'); + }); })->middleware(['jwt']); diff --git a/Server/application/cunkebao/controller/StatsController.php b/Server/application/cunkebao/controller/StatsController.php index acea1e6a..38fa441c 100644 --- a/Server/application/cunkebao/controller/StatsController.php +++ b/Server/application/cunkebao/controller/StatsController.php @@ -51,7 +51,6 @@ class StatsController extends Controller { $num = $this->request->param('num', 4); - $planScene = Db::name('plan_scene') ->field('id,name,image') ->where(['status' => 1]) @@ -196,4 +195,157 @@ class StatsController extends Controller return successJson($data, '获取成功'); } + + + + public function getFriendRequestTaskStats() + { + $companyId = $this->request->userInfo['companyId']; + $taskId = $this->request->param('taskId', ''); + if(empty($taskId)){ + return errorJson('任务id不能为空'); + } + + $task = Db::name('customer_acquisition_task')->where(['id' => $taskId, 'companyId' => $companyId,'deleteTime' => 0])->find(); + if(empty($task)){ + return errorJson('任务不存在或已删除'); + } + + + // 1. 获取startTime和endTime,格式是日期 + $startTime = $this->request->param('startTime', ''); + $endTime = $this->request->param('endTime', ''); + + // 如果获取不到则默认为7天的跨度 + if (empty($startTime)) { + $startTime = date('Y-m-d', time() - 86400 * 6); + } + if (empty($endTime)) { + $endTime = date('Y-m-d', time()); + } + + // 转换成时间戳格式 + $startTimestamp = strtotime($startTime . ' 00:00:00'); + $endTimestamp = strtotime($endTime . ' 23:59:59'); + + // 同时生成日期数组和时间戳二维数组 + $dateArray = []; + $timestampArray = []; + $currentTimestamp = $startTimestamp; + + while ($currentTimestamp <= $endTimestamp) { + // 生成日期格式数组 + $dateArray[] = date('m-d', $currentTimestamp); + + // 生成时间戳二维数组 + $dayStart = $currentTimestamp; + $dayEnd = strtotime('+1 day', $currentTimestamp) - 1; // 23:59:59 + $timestampArray[] = [$dayStart, $dayEnd]; + + $currentTimestamp = strtotime('+1 day', $currentTimestamp); + } + + + // 使用分组聚合统计,减少 SQL 次数 + $allRows = Db::name('task_customer') + ->field("FROM_UNIXTIME(createTime, '%m-%d') AS d, COUNT(*) AS c") + ->where(['task_id' => $taskId]) + ->where('createTime', 'between', [$startTimestamp, $endTimestamp]) + ->group('d') + ->select(); + + $successRows = Db::name('task_customer') + ->field("FROM_UNIXTIME(addTime, '%m-%d') AS d, COUNT(*) AS c") + ->where(['task_id' => $taskId]) + ->where('addTime', 'between', [$startTimestamp, $endTimestamp]) + ->whereIn('status', [1, 2, 4]) + ->group('d') + ->select(); + + $passRows = Db::name('task_customer') + ->field("FROM_UNIXTIME(passTime, '%m-%d') AS d, COUNT(*) AS c") + ->where(['task_id' => $taskId]) + ->where('passTime', 'between', [$startTimestamp, $endTimestamp]) + ->group('d') + ->select(); + + $errorRows = Db::name('task_customer') + ->field("FROM_UNIXTIME(updateTime, '%m-%d') AS d, COUNT(*) AS c") + ->where(['task_id' => $taskId, 'status' => 3]) + ->where('updateTime', 'between', [$startTimestamp, $endTimestamp]) + ->group('d') + ->select(); + + // 将分组结果映射到连续日期数组 + $mapToSeries = function(array $rows) use ($dateArray) { + $dict = []; + foreach ($rows as $row) { + // 兼容对象/数组两种返回 + $d = is_array($row) ? ($row['d'] ?? '') : ($row->d ?? ''); + $c = (int)(is_array($row) ? ($row['c'] ?? 0) : ($row->c ?? 0)); + if ($d !== '') { + $dict[$d] = $c; + } + } + $series = []; + foreach ($dateArray as $d) { + $series[] = $dict[$d] ?? 0; + } + return $series; + }; + + $allNumArray = $mapToSeries($allRows); + $successNumArray = $mapToSeries($successRows); + $passNumArray = $mapToSeries($passRows); + $errorNumArray = $mapToSeries($errorRows); + + // 计算通过率和成功率 + $passRateArray = []; + $successRateArray = []; + + for ($i = 0; $i < count($dateArray); $i++) { + // 通过率 = 通过数 / 总数 + $passRate = ($allNumArray[$i] > 0) ? round(($passNumArray[$i] / $allNumArray[$i]) * 100, 2) : 0; + $passRateArray[] = $passRate; + + // 成功率 = 成功数 / 总数 + $successRate = ($allNumArray[$i] > 0) ? round(($successNumArray[$i] / $allNumArray[$i]) * 100, 2) : 0; + $successRateArray[] = $successRate; + } + + // 计算总体统计 + $totalAll = array_sum($allNumArray); + $totalSuccess = array_sum($successNumArray); + $totalPass = array_sum($passNumArray); + $totalError = array_sum($errorNumArray); + + $totalPassRate = ($totalAll > 0) ? round(($totalPass / $totalAll) * 100, 2) : 0; + $totalSuccessRate = ($totalAll > 0) ? round(($totalSuccess / $totalAll) * 100, 2) : 0; + + // 返回结果 + $result = [ + 'startTime' => $startTime, + 'endTime' => $endTime, + 'dateArray' => $dateArray, + 'allNumArray' => $allNumArray, + 'successNumArray' => $successNumArray, + 'passNumArray' => $passNumArray, + 'errorNumArray' => $errorNumArray, + 'passRateArray' => $passRateArray, + 'successRateArray' => $successRateArray, + 'totalStats' => [ + 'totalAll' => $totalAll, + 'totalSuccess' => $totalSuccess, + 'totalPass' => $totalPass, + 'totalError' => $totalError, + 'totalPassRate' => $totalPassRate, + 'totalSuccessRate' => $totalSuccessRate + ] + ]; + + return successJson($result, '获取成功'); + } + + + } \ No newline at end of file diff --git a/Server/application/cunkebao/controller/WorkbenchController.php b/Server/application/cunkebao/controller/WorkbenchController.php index 69902f70..8f8c6d5f 100644 --- a/Server/application/cunkebao/controller/WorkbenchController.php +++ b/Server/application/cunkebao/controller/WorkbenchController.php @@ -4,6 +4,7 @@ namespace app\cunkebao\controller; use app\common\model\Device as DeviceModel; use app\common\model\DeviceWechatLogin as DeviceWechatLoginModel; +use app\common\model\WechatCustomer as WechatCustomerModel; use app\cunkebao\model\Workbench; use app\cunkebao\model\WorkbenchAutoLike; use app\cunkebao\model\WorkbenchMomentsSync; @@ -600,13 +601,20 @@ class WorkbenchController extends Controller ->field([ 'd.id', 'd.imei', 'd.memo', 'd.alive', 'l.wechatId', - 'a.nickname', 'a.alias', 'a.avatar', 'a.alias' + 'a.nickname', 'a.alias', 'a.avatar', 'a.alias', '0 totalFriend' ]) ->leftJoin('device_wechat_login l', 'd.id = l.deviceId and l.alive =' . DeviceWechatLoginModel::ALIVE_WECHAT_ACTIVE . ' and l.companyId = d.companyId') ->leftJoin('wechat_account a', 'l.wechatId = a.wechatId') ->whereIn('d.id', $workbench->config->deveiceGroups) ->order('d.id desc') ->select(); + + foreach ($deviceList as &$device) { + $curstomer = WechatCustomerModel::field('friendShip')->where(['wechatId' => $device['wechatId']])->find(); + $device['totalFriend'] = $curstomer->friendShip->totalFriend ?? 0; + } + unset($device); + $workbench->config->deveiceGroupsOptions = $deviceList; } else { $workbench->config->deveiceGroupsOptions = []; diff --git a/Server/application/cunkebao/controller/plan/GetAddFriendPlanDetailV1Controller.php b/Server/application/cunkebao/controller/plan/GetAddFriendPlanDetailV1Controller.php index 04ffac30..7ec88c07 100644 --- a/Server/application/cunkebao/controller/plan/GetAddFriendPlanDetailV1Controller.php +++ b/Server/application/cunkebao/controller/plan/GetAddFriendPlanDetailV1Controller.php @@ -4,6 +4,7 @@ namespace app\cunkebao\controller\plan; use app\common\model\Device as DeviceModel; use app\common\model\DeviceWechatLogin as DeviceWechatLoginModel; +use app\common\model\WechatCustomer as WechatCustomerModel; use library\ResponseHelper; use think\Controller; use think\Db; @@ -143,13 +144,18 @@ class GetAddFriendPlanDetailV1Controller extends Controller ->field([ 'd.id', 'd.imei', 'd.memo', 'd.alive', 'l.wechatId', - 'a.nickname', 'a.alias', '0 totalFriend' + 'a.nickname', 'a.alias', '0 totalFriend', '0 totalFriend' ]) ->leftJoin('device_wechat_login l', 'd.id = l.deviceId and l.alive =' . DeviceWechatLoginModel::ALIVE_WECHAT_ACTIVE . ' and l.companyId = d.companyId') ->leftJoin('wechat_account a', 'l.wechatId = a.wechatId') ->order('d.id desc') ->whereIn('d.id',$reqConf['deveiceGroups']) ->select(); + foreach ($deveiceGroupsOptions as &$device) { + $curstomer = WechatCustomerModel::field('friendShip')->where(['wechatId' => $device['wechatId']])->find(); + $device['totalFriend'] = $curstomer->friendShip->totalFriend ?? 0; + } + unset($device); $reqConf['deveiceGroupsOptions'] = $deveiceGroupsOptions; }else{ $reqConf['deveiceGroupsOptions'] = []; diff --git a/Server/application/cunkebao/controller/traffic/GetPotentialListWithInCompanyV1Controller.php b/Server/application/cunkebao/controller/traffic/GetPotentialListWithInCompanyV1Controller.php index 4085727d..e8f2260f 100644 --- a/Server/application/cunkebao/controller/traffic/GetPotentialListWithInCompanyV1Controller.php +++ b/Server/application/cunkebao/controller/traffic/GetPotentialListWithInCompanyV1Controller.php @@ -22,35 +22,49 @@ class GetPotentialListWithInCompanyV1Controller extends BaseController */ protected function makeWhere(array $params = []): array { - if (!empty($keyword = $this->request->param('keyword'))) { + $keyword = $this->request->param('keyword',''); + $device = $this->request->param('deviceId'); + $status = $this->request->param('addStatus',''); + $taskId = $this->request->param('taskId',''); + $packageId = $this->request->param('packageId',''); + $where = []; + if (!empty($keyword)) { $where[] = ['p.identifier|wa.nickname|wa.phone|wa.wechatId|wa.alias', 'like', '%' . $keyword . '%']; } // 状态筛选 - if ($status = $this->request->param('addStatus')) { - $where['s.status'] = $status; - } else { - $where['s.status'] = array('<>', TrafficSourceModel::STATUS_PASSED); + if (!empty($status)) { + if ($status == 1){ + $where[] = ['s.status','=',4]; + }elseif ($status == 2){ + $where[] = ['s.status','=',0]; + }elseif ($status == -1){ + $where[] = ['s.status','=',2]; + }elseif ($status == 3){ + $where[] = ['s.status','=',2]; + } } // 来源的筛选 - if ($fromd = $this->request->param('packageId')) { - if ($fromd != -1) { - $where['tsp.id'] = $fromd; + if ($packageId) { + if ($packageId != -1) { + $where[] = ['tsp.id','=',$packageId]; } else { - $where[] = ['tsp.id', null]; + $where[] = ['tsp.id','=', null]; } } - if ($device = $this->request->param('deviceId')) { - $where['d.deviceId'] = $device; + if (!empty($device)) { + $where[] = ['d.deviceId','=',$device]; } + if (!empty($taskId)){ + $where[] = ['t.sceneId','=',$taskId]; + } + $where[] = ['s.companyId','=',$this->getUserInfo('companyId')]; - $where['s.companyId'] = $this->getUserInfo('companyId'); - - return array_merge($where, $params); + return $where; } /** @@ -59,67 +73,58 @@ class GetPotentialListWithInCompanyV1Controller extends BaseController * @param array $where * @return \think\Paginator */ - protected function getPoolListByCompanyId(array $where) + protected function getPoolListByCompanyId(array $where,$isPage = true) { $query = TrafficPoolModel::alias('p') ->field( [ 'p.id', 'p.identifier', 'p.mobile', 'p.wechatId', 'p.identifier', 's.fromd', 's.status', 's.createTime', 's.companyId', 's.sourceId', 's.type', - 'wa.nickname', 'wa.avatar', 'wa.gender', 'wa.phone', + 'wa.nickname', 'wa.avatar', 'wa.gender', 'wa.phone','wa.alias' ] ) - ->join('traffic_source s', 'p.identifier=s.identifier', 'left') + ->join('traffic_source s', 'p.identifier=s.identifier') ->join('wechat_account wa', 'p.identifier=wa.wechatId', 'left') ->join('traffic_source_package_item tspi', 'p.identifier = tspi.identifier AND s.companyId = tspi.companyId', 'left') ->join('traffic_source_package tsp', 'tspi.packageId=tsp.id', 'left') ->join('device_wechat_login d', 's.sourceId=d.wechatId', 'left') - ->order('p.id DESC,s.id DESC') - ->group('p.identifier'); - - foreach ($where as $key => $value) { - if (is_numeric($key) && is_array($value) && isset($value[0]) && $value[0] === 'exp') { - $query->whereExp('', $value[1]); - continue; - } - if (is_array($value)) { - $query->where($key, ...$value); - continue; - } - $query->where($key, $value); - } - - $result = $query->paginate($this->request->param('limit/d', 10), false, ['page' => $this->request->param('page/d', 1)]); - $list = $result->items(); - $total = $result->total(); - - foreach ($list as &$item) { - //流量池筛选 - $package = Db::name('traffic_source_package_item')->alias('tspi') - ->join('traffic_source_package p', 'tspi.packageId=p.id AND tspi.companyId=p.companyId') - ->where(['tspi.companyId' => $item->companyId, 'tspi.identifier' => $item->identifier]) - ->column('p.name'); - - $package2 = Db::name('traffic_source_package_item')->alias('tspi') - ->join('traffic_source_package p', 'tspi.packageId=p.id') - ->where(['tspi.companyId' => $item->companyId, 'tspi.identifier' => $item->identifier, 'p.isSys' => 1]) - ->column('p.name'); - $packages = array_merge($package, $package2); - $item['packages'] = $packages; + ->where($where); - if ($item->type == 1) { - $tag = Db::name('wechat_friendship')->where(['wechatId' => $item->wechatId])->column('tags'); - $tags = []; - foreach ($tag as $k => $v) { - $v = json_decode($v, true); - if (!empty($v)) { - $tags = array_merge($tags, $v); + $result = $query->order('p.id DESC,s.id DESC')->group('p.identifier'); + + if ($isPage) { + $result = $query->paginate($this->request->param('limit/d', 10), false, ['page' => $this->request->param('page/d', 1)]); + $list = $result->items(); + $total = $result->total(); + }else{ + $list = $result->select(); + $total = ''; + } + + + if ($isPage) { + foreach ($list as &$item) { + //流量池筛选 + $package = Db::name('traffic_source_package_item')->alias('tspi') + ->join('traffic_source_package p', 'tspi.packageId=p.id AND tspi.companyId=p.companyId') + ->where(['tspi.identifier' => $item->identifier]) + ->whereIn('tspi.companyId', [0, $item->companyId]) + ->column('p.name'); + $item['packages'] = $package; + if ($item->type == 1) { + $tag = Db::name('wechat_friendship')->where(['wechatId' => $item->wechatId])->column('tags'); + $tags = []; + foreach ($tag as $k => $v) { + $v = json_decode($v, true); + if (!empty($v)) { + $tags = array_merge($tags, $v); + } } + $item['tags'] = $tags; } - $item['tags'] = $tags; - } + } } unset($item); $data = ['list' => $list, 'total' => $total]; @@ -356,6 +361,112 @@ class GetPotentialListWithInCompanyV1Controller extends BaseController return ResponseHelper::success(['wechat' => $tags, 'siteLabels' => $siteLabels]); } + + public function getPackage() + { + $companyId = $this->getUserInfo('companyId'); + $package = Db::name('traffic_source_package') + ->whereIn('companyId', [$companyId,0]) + ->field('id,name') + ->select(); + return ResponseHelper::success($package); + } + + + + public function addPackage() + { + try { + $addPackageId = $this->request->param('addPackageId',''); + $packageName = $this->request->param('packageName',''); + $userIds = $this->request->param('userIds',[]); + $companyId = $this->getUserInfo('companyId'); + $userId = $this->getUserInfo('id'); + if (empty($addPackageId) && empty($packageName)){ + return ResponseHelper::error('存储的流量池不能为空'); + } + + if (!empty($addPackageId)){ + $package = Db::name('traffic_source_package') + ->where(['id' => $addPackageId,'isDel' => 0]) + ->whereIn('companyId', [$companyId,0]) + ->field('id,name') + ->find(); + if (empty($package)){ + return ResponseHelper::error('该流量池不存在'); + } + $packageId = $package['id']; + }else{ + $package = Db::name('traffic_source_package') + ->where(['isDel' => 0,'name' => $packageName]) + ->whereIn('companyId', [$companyId,0]) + ->field('id,name') + ->find(); + if (!empty($package)){ + return ResponseHelper::error('该流量池名称已存在'); + } + $packageId = Db::name('traffic_source_package')->insertGetId(['userId' => $userId,'companyId' => $companyId,'name' => $packageName,'isDel' => 0]); + } + + + if (!empty($userIds)){ + if (!is_array($userIds)){ + return ResponseHelper::error('选择的用户类型错误'); + } + $result = Db::name('traffic_pool')->alias('tp') + ->join('traffic_source tc','tp.identifier=tc.identifier') + ->whereIn('tp.id',$userIds) + ->where(['companyId' => $companyId]) + ->group('tp.identifier') + ->column('tc.identifier'); + }else{ + $result = $this->getPoolListByCompanyId($this->makeWhere(),false); + $result = json_decode($result, true); + $result = array_column($result['list'],'identifier'); + } + + + + // 1000条为一组进行批量处理 + $batchSize = 1000; + $totalRows = count($result); + + for ($i = 0; $i < $totalRows; $i += $batchSize) { + $batchRows = array_slice($result, $i, $batchSize); + if (!empty($batchRows)) { + // 2. 批量查询已存在的手机 + $existingPhones = []; + $existing = Db::name('traffic_source_package_item') + ->where(['companyId' => $companyId,'packageId' => $packageId]) + ->whereIn('identifier', $batchRows) + ->field('identifier') + ->select(); + $existingPhones = array_column($existing, 'identifier'); + // 3. 过滤出新数据,批量插入 + $newData = []; + foreach ($batchRows as $row) { + if (!in_array($row, $existingPhones)) { + $newData[] = [ + 'packageId' => $packageId, + 'companyId' => $companyId, + 'identifier' => $row, + 'createTime' => time(), + ]; + } + } + // 4. 批量插入新数据 + if (!empty($newData)) { + Db::name('traffic_source_package_item')->insertAll($newData); + } + } + } + return ResponseHelper::success('添加成功'); + } catch (\Exception $e) { + return ResponseHelper::error($e->getMessage(), $e->getCode()); + } + } + + /* public function editUserTags() { $userId = $this->request->param('userId', ''); diff --git a/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php b/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php index 1998a070..7c347c42 100644 --- a/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php +++ b/Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php @@ -240,8 +240,9 @@ class Adapter implements WeChatServiceInterface ->where('id', $task['id']) ->update([ 'status' => $friendAddTaskCreated ? 1 : 3, - 'fail_reason' => $friendAddTaskCreated ? '' : '已经是好友了', + 'fail_reason' => '', 'processed_wechat_ids' => $task['processed_wechat_ids'], + 'addTime' => time(), 'updateTime' => time() ]); // ~~不用管,回头再添加再判断即可~~ @@ -298,7 +299,7 @@ class Adapter implements WeChatServiceInterface Db::name('task_customer') ->where('id', $task['id']) - ->update(['status' => 4, 'updateTime' => time()]); + ->update(['status' => 4,'passTime' => time(), 'updateTime' => time()]); $wechatFriendRecord = $this->getWeChatAccoutIdAndFriendIdByWeChatIdAndFriendPhone($passedWeChatId, $task['phone']); $msgConf = is_string($task_info['msgConf']) ? json_decode($task_info['msgConf'], 1) : $task_info['msgConf']; From 639d7d3cb6f91f20e92278ca268192aef4c0419b Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Fri, 15 Aug 2025 18:30:03 +0800 Subject: [PATCH 11/78] 1 --- .../api/controller/WebSocketController.php | 50 +++++++++++++++++++ Server/application/cunkebao/config/route.php | 3 +- .../cunkebao/controller/StatsController.php | 38 +++++++++++++- 3 files changed, 89 insertions(+), 2 deletions(-) diff --git a/Server/application/api/controller/WebSocketController.php b/Server/application/api/controller/WebSocketController.php index 24d67fa6..191e9f50 100644 --- a/Server/application/api/controller/WebSocketController.php +++ b/Server/application/api/controller/WebSocketController.php @@ -837,4 +837,54 @@ class WebSocketController extends BaseController return json_encode(['code' => 500, 'msg' => '邀请好友入群异常:' . $e->getMessage()]); } } + + /** + * 添加群好友 + * @param $data + * @return false|string + */ + public function CmdChatroomOperate($data = []) + { + try { + // 参数验证 + if (empty($data)) { + return json_encode(['code' => 400, 'msg' => '参数缺失']); + } + + // 验证必要参数 + if (empty($data['wechatId'])) { + return json_encode(['code' => 400, 'msg' => 'wechatId不能为空']); + } + + if (empty($data['sendWord'])) { + return json_encode(['code' => 400, 'msg' => '添加的招呼语不能为空']); + } + + if (empty($data['wechatAccountId'])) { + return json_encode(['code' => 400, 'msg' => '微信账号ID不能为空']); + } + if (empty($data['wechatChatroomId'])) { + return json_encode(['code' => 400, 'msg' => '群ID不能为空']); + } + + + // 构建请求参数 + $params = [ + "chatroomOperateType" => 1, + "cmdType" => "CmdChatroomOperate", + "extra" => [ + 'wechatId' => $data['wechatId'], + 'sendWord' => $data['sendWord'] + ], + "seq" => time(), + "wechatAccountId" => $data['wechatAccountId'], + "wechatChatroomId" => $data['wechatChatroomId'], + ]; + $message = $this->sendMessage($params); + return json_encode(['code' => 200, 'msg' => '添加好友请求发送成功', 'data' => $message]); + } catch (\Exception $e) { + // 返回错误响应 + return json_encode(['code' => 500, 'msg' => '添加群好友异常:' . $e->getMessage()]); + } + } } \ No newline at end of file diff --git a/Server/application/cunkebao/config/route.php b/Server/application/cunkebao/config/route.php index 6e0ebd8f..5b93d1c0 100644 --- a/Server/application/cunkebao/config/route.php +++ b/Server/application/cunkebao/config/route.php @@ -130,13 +130,14 @@ Route::group('v1/', function () { }); - //数据统计相关 + //数据统计相关 Route::group('dashboard',function (){ Route::get('', 'app\cunkebao\controller\StatsController@baseInfoStats'); Route::get('plan-stats', 'app\cunkebao\controller\StatsController@planStats'); Route::get('sevenDay-stats', 'app\cunkebao\controller\StatsController@customerAcquisitionStats7Days'); Route::get('today-stats', 'app\cunkebao\controller\StatsController@todayStats'); Route::get('friendRequestTaskStats', 'app\cunkebao\controller\StatsController@getFriendRequestTaskStats'); + Route::get('userInfoStats', 'app\cunkebao\controller\StatsController@userInfoStats'); }); diff --git a/Server/application/cunkebao/controller/StatsController.php b/Server/application/cunkebao/controller/StatsController.php index 38fa441c..a47f80c8 100644 --- a/Server/application/cunkebao/controller/StatsController.php +++ b/Server/application/cunkebao/controller/StatsController.php @@ -197,7 +197,13 @@ class StatsController extends Controller } - + /** + * 场景获客数据统计 + * @return \think\response\Json + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ public function getFriendRequestTaskStats() { $companyId = $this->request->userInfo['companyId']; @@ -347,5 +353,35 @@ class StatsController extends Controller } + public function userInfoStats() + { + $companyId = $this->request->userInfo['companyId']; + $userId = $this->request->userInfo['id']; + $isAdmin = $this->request->userInfo['isAdmin']; + + + $device = Db::name('device')->where(['companyId' => $companyId,'deleteTime' => 0]); + $wechat = Db::name('wechat_customer')->where(['companyId' => $companyId]); + $contentLibrary = Db::name('content_library')->where(['companyId' => $companyId,'isDel' => 0]); + $user = Db::name('wechat_friendship')->where(['companyId' => $companyId,'deleteTime' => 0]); + + if(empty($isAdmin)){ + $contentLibrary = $contentLibrary->where(['userId' => $userId]); + } + + $deviceNum = $device->count(); + $wechatNum = $wechat->count(); + $contentLibraryNum = $contentLibrary->count(); + $userNum = $user->count(); + + $data = [ + 'deviceNum' => $deviceNum, + 'wechatNum' => $wechatNum, + 'contentLibraryNum' => $contentLibraryNum, + 'userNum' => $userNum, + ]; + return successJson($data, '获取成功'); + } + } \ No newline at end of file From d93165bfac7e29469b38a187400d1d5ea8c5f5c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B6=85=E7=BA=A7=E8=80=81=E7=99=BD=E5=85=94?= Date: Wed, 20 Aug 2025 11:19:53 +0800 Subject: [PATCH 12/78] =?UTF-8?q?feat(=E8=87=AA=E5=8A=A8=E5=BB=BA=E7=BE=A4?= =?UTF-8?q?):=20=E6=B7=BB=E5=8A=A0=E5=88=A0=E9=99=A4=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E5=BB=BA=E7=BE=A4=E4=BB=BB=E5=8A=A1=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增删除自动建群任务的API接口及前端交互逻辑,使用自定义确认弹窗替代原生confirm,提供更好的用户体验和错误处理 --- .../mobile/workspace/auto-group/list/api.ts | 5 ++++ .../workspace/auto-group/list/index.tsx | 30 +++++++++++++++---- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/Cunkebao/src/pages/mobile/workspace/auto-group/list/api.ts b/Cunkebao/src/pages/mobile/workspace/auto-group/list/api.ts index 8ad39e81..fede6244 100644 --- a/Cunkebao/src/pages/mobile/workspace/auto-group/list/api.ts +++ b/Cunkebao/src/pages/mobile/workspace/auto-group/list/api.ts @@ -9,3 +9,8 @@ export const getAutoGroupList = (params: any) => export function copyAutoGroupTask(id: string): Promise { return request("/v1/workbench/copy", { id }, "POST"); } + +// 删除自动建群任务 +export function deleteAutoGroupTask(id: string): Promise { + return request("/v1/workbench/delete", { id }, "DELETE"); +} diff --git a/Cunkebao/src/pages/mobile/workspace/auto-group/list/index.tsx b/Cunkebao/src/pages/mobile/workspace/auto-group/list/index.tsx index 8e44c600..f19cc732 100644 --- a/Cunkebao/src/pages/mobile/workspace/auto-group/list/index.tsx +++ b/Cunkebao/src/pages/mobile/workspace/auto-group/list/index.tsx @@ -14,7 +14,12 @@ import { PlusOutlined, SearchOutlined, } from "@ant-design/icons"; -import { getAutoGroupList, copyAutoGroupTask } from "./api"; +import { + getAutoGroupList, + copyAutoGroupTask, + deleteAutoGroupTask, +} from "./api"; +import { comfirm } from "@/utils/common"; import Layout from "@/components/Layout/Layout"; import style from "./index.module.scss"; import NavCommon from "@/components/NavCommon"; @@ -110,14 +115,27 @@ const AutoGroupList: React.FC = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - const handleDelete = (taskId: string) => { + const handleDelete = async (taskId: string) => { const taskToDelete = tasks.find(task => task.id === taskId); if (!taskToDelete) return; - window.confirm(`确定要删除"${taskToDelete.name}"吗?`) && - setTasks(tasks.filter(task => task.id !== taskId)); - Toast.show({ content: "删除成功" }); - }; + try { + await comfirm("确定要删除吗?", { + title: "删除确认", + cancelText: "取消", + confirmText: "删除", + }); + + await deleteAutoGroupTask(taskId); + setTasks(tasks.filter(task => task.id !== taskId)); + Toast.show({ content: "删除成功" }); + } catch (error) { + // 用户取消删除或删除失败 + if (error !== "cancel") { + Toast.show({ content: "删除失败" }); + } + } + }; const handleEdit = (taskId: string) => { navigate(`/workspace/auto-group/${taskId}/edit`); }; From fcc48052207d4ef6c34f08cc48bd5b38be43c684 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B6=85=E7=BA=A7=E8=80=81=E7=99=BD=E5=85=94?= Date: Wed, 20 Aug 2025 15:07:17 +0800 Subject: [PATCH 13/78] =?UTF-8?q?feat(=E8=87=AA=E5=8A=A8=E5=BB=BA=E7=BE=A4?= =?UTF-8?q?):=20=E6=B7=BB=E5=8A=A0=E5=86=85=E5=AE=B9=E5=BA=93=E9=80=89?= =?UTF-8?q?=E6=8B=A9=E5=8A=9F=E8=83=BD=E5=B9=B6=E9=87=8D=E6=9E=84=E8=A1=A8?= =?UTF-8?q?=E5=8D=95=E4=B8=BA=E5=88=86=E6=AD=A5=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加内容库选择组件及相关类型定义 重构自动建群表单为分步式操作流程 新增基础设置、设备选择和内容选择三个步骤组件 完善表单验证逻辑和步骤导航功能 --- .../form/components/BasicSettings.tsx | 291 ++++++++++++ .../form/components/ContentSelector.tsx | 95 ++++ .../form/components/DeviceSelector.tsx | 95 ++++ .../workspace/auto-group/form/index.tsx | 420 +++++++++--------- .../mobile/workspace/auto-group/form/types.ts | 15 + 5 files changed, 694 insertions(+), 222 deletions(-) create mode 100644 Cunkebao/src/pages/mobile/workspace/auto-group/form/components/BasicSettings.tsx create mode 100644 Cunkebao/src/pages/mobile/workspace/auto-group/form/components/ContentSelector.tsx create mode 100644 Cunkebao/src/pages/mobile/workspace/auto-group/form/components/DeviceSelector.tsx diff --git a/Cunkebao/src/pages/mobile/workspace/auto-group/form/components/BasicSettings.tsx b/Cunkebao/src/pages/mobile/workspace/auto-group/form/components/BasicSettings.tsx new file mode 100644 index 00000000..9cba69e9 --- /dev/null +++ b/Cunkebao/src/pages/mobile/workspace/auto-group/form/components/BasicSettings.tsx @@ -0,0 +1,291 @@ +import React, { useImperativeHandle, forwardRef } from "react"; +import { Input, Button, Card, Switch, Form, InputNumber } from "antd"; +import { TextArea } from "antd-mobile"; + +interface BasicSettingsProps { + defaultValues?: { + name: string; + startTime: string; + endTime: string; + groupSizeMin: number; + groupSizeMax: number; + maxGroupsPerDay: number; + groupNameTemplate: string; + groupDescription: string; + status: number; + }; +} + +export interface BasicSettingsRef { + validate: () => Promise; + getValues: () => any; +} + +const BasicSettings = forwardRef( + ({ + defaultValues = { + name: "", + startTime: "06:00", + endTime: "23:59", + groupSizeMin: 20, + groupSizeMax: 50, + maxGroupsPerDay: 10, + groupNameTemplate: "", + groupDescription: "", + status: 1, + }, + }, ref) => { + const [form] = Form.useForm(); + + // 暴露方法给父组件 + useImperativeHandle(ref, () => ({ + validate: async () => { + try { + await form.validateFields(); + return true; + } catch (error) { + console.log("BasicSettings 表单验证失败:", error); + return false; + } + }, + getValues: () => { + return form.getFieldsValue(); + }, + })); + + return ( +
+ +
{ + // 可以在这里处理表单值变化 + }} + > + {/* 任务名称 */} + + + + + {/* 允许建群的时间段 */} + +
+ + + + + + + +
+
+ + {/* 每日最大建群数 */} + +
+ + + +
+
+ + {/* 群组最小人数 */} + +
+ + + +
+
+ + {/* 群组最大人数 */} + +
+ + + +
+
+ + {/* 群名称模板 */} + + + + + {/* 群描述 */} + +