Merge branch 'develop' into yongpxu-dev

# Conflicts:
#	Server/application/command/SwitchFriendsCommand.php   resolved by develop version
#	Server/application/cunkebao/controller/ContentLibraryController.php   resolved by develop version
#	Server/application/cunkebao/controller/WorkbenchController.php   resolved by develop version
#	Server/application/cunkebao/controller/plan/PlanSceneV1Controller.php   resolved by develop version
#	Server/application/job/WechatMomentsJob.php   resolved by develop version
#	Server/application/job/WorkbenchAutoLikeJob.php   resolved by develop version
#	Server/extend/WeChatDeviceApi/Adapters/ChuKeBao/Adapter.php   resolved by develop version
This commit is contained in:
笔记本里的永平
2025-07-16 11:08:50 +08:00
19 changed files with 1092 additions and 149 deletions

View File

@@ -4,9 +4,11 @@ namespace app\cunkebao\controller;
use app\cunkebao\model\ContentLibrary;
use app\cunkebao\model\ContentItem;
use library\s2\titleFavicon;
use think\Controller;
use think\Db;
use app\api\controller\WebSocketController;
use think\facade\Cache;
use think\facade\Env;
use app\api\controller\AutomaticAssign;
@@ -329,7 +331,7 @@ class ContentLibraryController extends Controller
$library->timeEnd = isset($param['endTime']) ? strtotime($param['endTime']) : 0;
$library->status = isset($param['status']) ? $param['status'] : 0;
$library->updateTime = time();
$library->save();
@@ -418,7 +420,7 @@ class ContentLibraryController extends Controller
// 查询数据
$list = ContentItem::where($where)
->order('createTime', 'desc')
->order('createMomentTime DESC,createTime DESC')
->page($page, $limit)
->select();
@@ -751,7 +753,26 @@ class ContentLibraryController extends Controller
/************************************
* 数据采集相关功能
************************************/
function getExternalPageDetails($url)
{
$html = file_get_contents($url);
$dom = new \DOMDocument();
@$dom->loadHTML($html);
$xpath = new \DOMXPath($dom);
// 获取标题
$titleNode = $xpath->query('//title');
$title = $titleNode->length > 0 ? $titleNode->item(0)->nodeValue : '';
// 获取图标链接
$iconNode = $xpath->query('//link[@rel="shortcut icon"]/@href');
$icon = $iconNode->length > 0 ? $iconNode->item(0)->nodeValue : '';
return ['title' => $title, 'icon' => $icon];
}
/**
* 执行朋友圈采集任务
* @return \think\response\Json
@@ -761,7 +782,7 @@ class ContentLibraryController extends Controller
// 查询条件:未删除且已开启的内容库
$where = [
['isDel', '=', 0], // 未删除
['status', '=', 1] // 已开启
['status', '=', 1], // 已开启
];
// 查询符合条件的内容库
@@ -862,10 +883,11 @@ class ContentLibraryController extends Controller
'message' => '没有指定要采集的好友'
];
}
$friendData = [];
try {
$toAccountId = '';
$username = Env::get('api.username', '');
$password = Env::get('api.password', '');
$username = Env::get('api.username2', '');
$password = Env::get('api.password2', '');
if (!empty($username) || !empty($password)) {
$toAccountId = Db::name('users')->where('account',$username)->value('s2_accountId');
}
@@ -889,10 +911,14 @@ class ContentLibraryController extends Controller
$totalMomentsCount = 0;
foreach ($friends as $friend) {
$friendData = $friend;
if (!empty($username) && !empty($password)) {
//执行切换好友命令
$automaticAssign = new AutomaticAssign();
$automaticAssign->allotWechatFriend(['wechatFriendId' => $friend['id'],'toAccountId' => $toAccountId],true);
//存入缓存
$friendData['friendId'] = $friend['id'];
artificialAllotWechatFriend($friendData);
//执行采集朋友圈命令
$webSocket = new WebSocketController(['userName' => $username,'password' => $password,'accountId' => $toAccountId]);
$webSocket->getMoments(['wechatFriendId' => $friend['id'],'wechatAccountId' => $friend['wechatAccountId']]);
@@ -1389,9 +1415,56 @@ class ContentLibraryController extends Controller
$coverImage = $resUrls[0];
}
// 判断内容类型 (0=未知, 1=图片, 2=链接, 3=视频, 4=文本, 5=小程序, 6=图文)
$contentType = $this->determineContentType($moment['content'] ?? '', $resUrls, $urls);
// 判断内容类型 (0=未知, 1=图片, 2=链接, 3=视频, 4=文本, 5=小程序)
if($moment['type'] == 1) {
//图文
$contentType = 1;
}elseif ($moment['type'] == 3){
//链接
$contentType = 2;
$urls = [];
$url = is_string($moment['urls']) ? json_decode($moment['urls'], true) : $moment['urls'] ?? [];
$url = $url[0];
// 检查是否是飞书链接
if (strpos($url, 'feishu.cn') !== false) {
// 飞书文档需要登录,无法直接获取内容,返回默认信息
$urls[] = [
'url' => $url,
'image' => 'http://karuosiyujzk.oss-cn-shenzhen.aliyuncs.com/2025/07/09/3db2a5d7fe49011ab68175a42a5094ce.jpeg',
'desc' => '飞书文档'
];
}else{
$getUrlDetails = $this->getExternalPageDetails($url);
$icon = 'http://karuosiyujzk.oss-cn-shenzhen.aliyuncs.com/2025/07/09/ec039d96fad6eab1d960f207d3d9ca9f.jpeg';
if (!empty($getUrlDetails['title'])) {
$urls[] = [
'url' => $url,
'image' => $icon,
'desc' => '点击查看详情'
];
}else{
$urls[] = [
'url' => $url,
'image' => !empty($getUrlDetails['icon']) ? $getUrlDetails['icon'] : $icon,
'desc' => $getUrlDetails['title']
];
}
}
$moment['urls'] = $urls;
}elseif ($moment['type'] == 15){
//视频
$contentType = 3;
}elseif ($moment['type'] == 2){
//纯文本
$contentType = 4;
}elseif ($moment['type'] == 30){
//小程序
$contentType = 5;
}else{
$contentType = 1;
}
// 如果不存在,则创建新的内容项目
$item = new ContentItem();
$item->libraryId = $libraryId;
@@ -1695,4 +1768,167 @@ class ContentLibraryController extends Controller
return [];
}
}
}
/**
* 解析URL获取网页信息内部调用
* @param string $url 要解析的URL
* @return array 包含title、icon的数组失败返回空数组
*/
public function parseUrl($url)
{
if (empty($url) || !filter_var($url, FILTER_VALIDATE_URL)) {
return [];
}
try {
// 设置请求头,模拟浏览器访问
$context = stream_context_create([
'http' => [
'method' => 'GET',
'header' => [
'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language: zh-CN,zh;q=0.9,en;q=0.8',
'Accept-Encoding: gzip, deflate',
'Connection: keep-alive',
'Upgrade-Insecure-Requests: 1'
],
'timeout' => 10,
'follow_location' => true,
'max_redirects' => 3
]
]);
// 获取网页内容
$html = @file_get_contents($url, false, $context);
if ($html === false) {
return [];
}
// 检测编码并转换为UTF-8
$encoding = mb_detect_encoding($html, ['UTF-8', 'GBK', 'GB2312', 'BIG5', 'ASCII']);
if ($encoding && $encoding !== 'UTF-8') {
$html = mb_convert_encoding($html, 'UTF-8', $encoding);
}
// 解析HTML
$dom = new \DOMDocument();
@$dom->loadHTML($html, LIBXML_NOERROR | LIBXML_NOWARNING);
$xpath = new \DOMXPath($dom);
$result = [
'title' => '',
'icon' => '',
'url' => $url
];
// 提取标题
$titleNodes = $xpath->query('//title');
if ($titleNodes->length > 0) {
$result['title'] = trim($titleNodes->item(0)->textContent);
}
// 提取图标 - 优先获取favicon
$iconNodes = $xpath->query('//link[@rel="icon"]/@href | //link[@rel="shortcut icon"]/@href | //link[@rel="apple-touch-icon"]/@href');
if ($iconNodes->length > 0) {
$iconUrl = trim($iconNodes->item(0)->value);
$result['icon'] = $this->makeAbsoluteUrl($iconUrl, $url);
} else {
// 尝试获取Open Graph图片
$ogImageNodes = $xpath->query('//meta[@property="og:image"]/@content');
if ($ogImageNodes->length > 0) {
$result['icon'] = trim($ogImageNodes->item(0)->value);
} else {
// 默认favicon路径
$result['icon'] = $this->makeAbsoluteUrl('/favicon.ico', $url);
}
}
// 清理和验证数据
$result['title'] = $this->cleanText($result['title']);
return $result;
} catch (\Exception $e) {
// 记录错误日志但不抛出异常
\think\facade\Log::error('URL解析失败: ' . $e->getMessage() . ' URL: ' . $url);
return [];
}
}
/**
* 将相对URL转换为绝对URL
* @param string $relativeUrl 相对URL
* @param string $baseUrl 基础URL
* @return string 绝对URL
*/
private function makeAbsoluteUrl($relativeUrl, $baseUrl)
{
if (empty($relativeUrl)) {
return '';
}
// 如果已经是绝对URL直接返回
if (filter_var($relativeUrl, FILTER_VALIDATE_URL)) {
return $relativeUrl;
}
// 解析基础URL
$baseParts = parse_url($baseUrl);
if (!$baseParts) {
return $relativeUrl;
}
// 处理以/开头的绝对路径
if (strpos($relativeUrl, '/') === 0) {
return $baseParts['scheme'] . '://' . $baseParts['host'] .
(isset($baseParts['port']) ? ':' . $baseParts['port'] : '') .
$relativeUrl;
}
// 处理相对路径
$basePath = isset($baseParts['path']) ? dirname($baseParts['path']) : '/';
if ($basePath === '.') {
$basePath = '/';
}
return $baseParts['scheme'] . '://' . $baseParts['host'] .
(isset($baseParts['port']) ? ':' . $baseParts['port'] : '') .
$basePath . '/' . $relativeUrl;
}
/**
* 清理文本内容
* @param string $text 要清理的文本
* @return string 清理后的文本
*/
private function cleanText($text)
{
if (empty($text)) {
return '';
}
// 移除HTML实体
$text = html_entity_decode($text, ENT_QUOTES | ENT_HTML5, 'UTF-8');
// 移除多余的空白字符
$text = preg_replace('/\s+/', ' ', $text);
// 移除控制字符
$text = preg_replace('/[\x00-\x1F\x7F]/', '', $text);
return trim($text);
}
}

View File

@@ -2,6 +2,8 @@
namespace app\cunkebao\controller;
use app\common\model\Device as DeviceModel;
use app\common\model\DeviceWechatLogin as DeviceWechatLoginModel;
use app\cunkebao\model\Workbench;
use app\cunkebao\model\WorkbenchAutoLike;
use app\cunkebao\model\WorkbenchMomentsSync;
@@ -236,10 +238,16 @@ class WorkbenchController extends Controller
$item->config = $item->momentsSync;
$item->config->devices = json_decode($item->config->devices, true);
$item->config->contentLibraries = json_decode($item->config->contentLibraries, true);
//同步记录
$sendNum = Db::name('workbench_moments_sync_item')->where(['workbenchId' => $item->id])->count();
$item->syncCount = $sendNum;
$lastTime = Db::name('workbench_moments_sync_item')->where(['workbenchId' => $item->id])->order('id DESC')->value('createTime');
$item->lastSyncTime = !empty($lastTime) ? date('Y-m-d H:i',$lastTime) : '--';
// 获取内容库名称
if (!empty($item->config->contentLibraries)) {
$libraryNames = ContentLibrary::where('id', 'in', $item->config->contentLibraries)
$libraryNames = ContentLibrary::whereIn('id', $item->config->contentLibraries)
->column('name');
$item->config->contentLibraryNames = $libraryNames;
} else {
@@ -426,6 +434,42 @@ class WorkbenchController extends Controller
$workbench->config = $workbench->momentsSync;
$workbench->config->devices = json_decode($workbench->config->devices, true);
$workbench->config->contentLibraries = json_decode($workbench->config->contentLibraries, true);
//同步记录
$sendNum = Db::name('workbench_moments_sync_item')->where(['workbenchId' => $workbench->id])->count();
$workbench->syncCount = $sendNum;
$lastTime = Db::name('workbench_moments_sync_item')->where(['workbenchId' => $workbench->id])->order('id DESC')->value('createTime');
$workbench->lastSyncTime = !empty($lastTime) ? date('Y-m-d H:i',$lastTime) : '--';
// 获取内容库名称
if (!empty($workbench->config->contentLibraries)) {
$libraryNames = ContentLibrary::where('id', 'in', $workbench->config->contentLibraries)
->select();
$workbench->config->contentLibraries = $libraryNames;
} else {
$workbench->config->contentLibraryNames = [];
}
if(!empty($workbench->config->devices)){
$deviceList = DeviceModel::alias('d')
->field([
'd.id', 'd.imei', 'd.memo', 'd.alive',
'l.wechatId',
'a.nickname', 'a.alias', 'a.avatar','a.alias'
])
->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->devices)
->order('d.id desc')
->select();
$workbench->config->deviceList = $deviceList;
}else{
$workbench->config->deviceList = [];
}
unset($workbench->momentsSync,$workbench->moments_sync);
}
break;
@@ -635,6 +679,18 @@ class WorkbenchController extends Controller
case self::TYPE_MOMENTS_SYNC:
$config = WorkbenchMomentsSync::where('workbenchId', $param['id'])->find();
if ($config) {
if (!empty($param['contentLibraries'])){
foreach ($param['contentLibraries'] as $library){
if(isset($library['id']) && !empty($library['id'])){
$contentLibraries[] = $library['id'];
}else{
$contentLibraries[] = $library;
}
}
}else{
$contentLibraries = [];
}
$config->syncInterval = $param['syncInterval'];
$config->syncCount = $param['syncCount'];
$config->syncType = $param['syncType'];
@@ -642,7 +698,7 @@ class WorkbenchController extends Controller
$config->endTime = $param['endTime'];
$config->accountType = $param['accountType'];
$config->devices = json_encode($param['devices']);
$config->contentLibraries = json_encode($param['contentLibraries'] ?? []);
$config->contentLibraries = json_encode($contentLibraries);
$config->updateTime = time();
$config->save();
}

View File

@@ -21,25 +21,31 @@ class GetChatroomListV1Controller extends BaseController
$limit = $this->request->param('limit', 20);
$keyword = $this->request->param('keyword', '');
try {
$wechatIds = Db::name('device')->alias('d')
->join('device_wechat_login dwl','dwl.deviceId=d.id AND dwl.companyId='.$this->getUserInfo('companyId'))
->where(['d.companyId' => $this->getUserInfo('companyId'),'d.deleteTime' => 0])
->column('dwl.wechatId');
$where = [];
if ($this->getUserInfo('isAdmin') == 1) {
$where[] = ['g.companyId', '=', $this->getUserInfo('companyId')];
$where[] = ['g.deleteTime', '=', 0];
$where[] = ['g.ownerWechatId', 'in', $wechatIds];
} else {
$where[] = ['g.companyId', '=', $this->getUserInfo('companyId')];
$where[] = ['g.deleteTime', '=', 0];
$where[] = ['g.ownerWechatId', 'in', $wechatIds];
//$where[] = ['g.userId', '=', $this->getUserInfo('id')];
}
if(!empty($keyword)){
$where[] = ['g.name', 'like', '%'.$keyword.'%'];
}
$data = WechatChatroom::alias('g')
->field(['g.id', 'g.chatroomId', 'g.name', 'g.avatar','g.ownerWechatId', 'g.identifier', 'g.createTime',
'wa.nickname as ownerNickname','wa.avatar as ownerAvatar','wa.alias as ownerAlias'])
->Join('wechat_account wa', 'g.ownerWechatId = wa.wechatId', 'LEFT')
->join('wechat_account wa', 'g.ownerWechatId = wa.wechatId', 'LEFT')
->where($where);
$total = $data->count();

View File

@@ -38,23 +38,21 @@ class GetFriendListV1Controller extends BaseController
$where[] = ['isDeleted','=',0];
}
if(!empty($keyword)){
$where[] = ['nickname|alias|wechatId','like','%'.$keyword.'%'];
}
$wechatIds = Db::name('device')->alias('d')
->join('device_wechat_login dwl','dwl.deviceId=d.id AND dwl.companyId='.$this->getUserInfo('companyId'))
->where(['d.companyId' => $this->getUserInfo('companyId'),'d.deleteTime' => 0]);
$devices = Db::name('device_wechat_login')
->where(['companyId' => $this->getUserInfo('companyId'),'alive' => 1])
->order('id desc')
->group('wechatId');
if(!empty($deviceIds) && is_array($deviceIds)){
$devices = $devices->whereIn('deviceId',$deviceIds);
if (!empty($deviceIds)){
$wechatIds = $wechatIds->where('d.id','in',$deviceIds);
}
$devices = $devices->column('wechatId');
$wechatIds = $wechatIds->column('dwl.wechatId');
$where[] = ['ownerWechatId','in',$devices];
$where[] = ['ownerWechatId','in',$wechatIds];
$data = Db::table('s2_wechat_friend')
->field(['nickname','avatar','alias','id','wechatId','ownerNickname','ownerAlias','ownerWechatId','createTime'])

View File

@@ -5,6 +5,8 @@ namespace app\cunkebao\controller\plan;
use library\ResponseHelper;
use think\Db;
use app\cunkebao\controller\BaseController;
use app\cunkebao\controller\plan\PosterWeChatMiniProgram;
/**
* 获取计划任务列表控制器
*/
@@ -54,18 +56,18 @@ class PlanSceneV1Controller extends BaseController
$val['acquiredCount'] = Db::name('task_customer')->where('task_id',$val['id'])->count();
$val['addedCount'] = Db::name('task_customer')->where('task_id',$val['id'])->whereIn('status',[1,2,3,4])->count();
$val['passCount'] = Db::name('task_customer')->where('task_id',$val['id'])->where('status',4)->count();
$val['passRate'] = 0;
if(!empty($val['passCount']) && !empty($val['addedCount'])){
$passRate = ($val['addedCount'] / $val['passCount']) * 100;
$passRate = ($val['passCount'] / $val['addedCount']) * 100;
$val['passRate'] = number_format($passRate,2);
}
$lastTime = Db::name('task_customer')->where(['task_id'=>$val['id']])->max('updated_at');
$val['lastUpdated'] = !empty($lastTime) ? date('Y-m-d H:i', $lastTime) : '--';
}
unset($val);
return ResponseHelper::success([
'total' => $total,
'list' => $list
@@ -75,41 +77,6 @@ class PlanSceneV1Controller extends BaseController
}
}
/**
* 拷贝计划任务
*
* @return \think\response\Json
*/
public function copy()
{
try {
$params = $this->request->param();
$planId = isset($params['planId']) ? intval($params['planId']) : 0;
if ($planId <= 0) {
return ResponseHelper::error('计划ID不能为空', 400);
}
$plan = Db::name('customer_acquisition_task')->where('id', $planId)->find();
if (!$plan) {
return ResponseHelper::error('计划不存在', 404);
}
unset($plan['id']);
$plan['name'] = $plan['name'] . ' (拷贝)';
$plan['createTime'] = time();
$plan['updateTime'] = time();
$newPlanId = Db::name('customer_acquisition_task')->insertGetId($plan);
if (!$newPlanId) {
return ResponseHelper::error('拷贝计划失败', 500);
}
return ResponseHelper::success(['planId' => $newPlanId], '拷贝计划任务成功');
} catch (\Exception $e) {
return ResponseHelper::error('系统错误: ' . $e->getMessage(), 500);
}
}
/**
* 删除计划任务
@@ -163,4 +130,273 @@ class PlanSceneV1Controller extends BaseController
return ResponseHelper::error('系统错误: ' . $e->getMessage(), 500);
}
}
/**
* 获取获客计划设备列表
*
* @return \think\response\Json
*/
public function getPlanDevices()
{
try {
$params = $this->request->param();
$planId = isset($params['planId']) ? intval($params['planId']) : 0;
$page = isset($params['page']) ? intval($params['page']) : 1;
$limit = isset($params['limit']) ? intval($params['limit']) : 10;
$deviceStatus = isset($params['deviceStatus']) ? $params['deviceStatus'] : '';
$searchKeyword = isset($params['searchKeyword']) ? trim($params['searchKeyword']) : '';
// 验证计划ID
if ($planId <= 0) {
return ResponseHelper::error('计划ID不能为空', 400);
}
// 验证计划是否存在且用户有权限
$plan = Db::name('customer_acquisition_task')
->where([
'id' => $planId,
'deleteTime' => 0,
'companyId' => $this->getUserInfo('companyId')
])
->find();
if (!$plan) {
return ResponseHelper::error('计划不存在或无权限访问', 404);
}
// 如果是管理员,需要验证用户权限
if ($this->getUserInfo('isAdmin')) {
$userPlan = Db::name('customer_acquisition_task')
->where([
'id' => $planId,
'userId' => $this->getUserInfo('id')
])
->find();
if (!$userPlan) {
return ResponseHelper::error('您没有权限访问该计划', 403);
}
}
// 构建查询条件
$where = [
'pt.plan_id' => $planId,
'd.deleteTime' => 0,
'd.companyId' => $this->getUserInfo('companyId')
];
// 设备状态筛选
if (!empty($deviceStatus)) {
$where['d.alive'] = $deviceStatus;
}
// 搜索关键词
$searchWhere = [];
if (!empty($searchKeyword)) {
$searchWhere[] = ['d.imei', 'like', "%{$searchKeyword}%"];
$searchWhere[] = ['d.memo', 'like', "%{$searchKeyword}%"];
}
// 查询设备总数
$totalQuery = Db::name('plan_task_device')->alias('pt')
->join('device d', 'pt.device_id = d.id')
->where($where);
if (!empty($searchWhere)) {
$totalQuery->where(function ($query) use ($searchWhere) {
foreach ($searchWhere as $condition) {
$query->whereOr($condition[0], $condition[1], $condition[2]);
}
});
}
$total = $totalQuery->count();
// 查询设备列表
$listQuery = Db::name('plan_task_device')->alias('pt')
->join('device d', 'pt.device_id = d.id')
->field([
'd.id',
'd.imei',
'd.memo',
'd.alive',
'd.extra',
'd.createTime',
'd.updateTime',
'pt.status as plan_device_status',
'pt.createTime as assign_time'
])
->where($where)
->order('pt.createTime', 'desc');
if (!empty($searchWhere)) {
$listQuery->where(function ($query) use ($searchWhere) {
foreach ($searchWhere as $condition) {
$query->whereOr($condition[0], $condition[1], $condition[2]);
}
});
}
$list = $listQuery->page($page, $limit)->select();
// 处理设备数据
foreach ($list as &$device) {
// 格式化时间
$device['createTime'] = date('Y-m-d H:i:s', $device['createTime']);
$device['updateTime'] = date('Y-m-d H:i:s', $device['updateTime']);
$device['assign_time'] = date('Y-m-d H:i:s', $device['assign_time']);
// 解析设备额外信息
if (!empty($device['extra'])) {
$extra = json_decode($device['extra'], true);
$device['battery'] = isset($extra['battery']) ? intval($extra['battery']) : 0;
$device['device_info'] = $extra;
} else {
$device['battery'] = 0;
$device['device_info'] = [];
}
// 设备状态文本
$device['alive_text'] = $this->getDeviceStatusText($device['alive']);
$device['plan_device_status_text'] = $this->getPlanDeviceStatusText($device['plan_device_status']);
// 获取设备当前微信登录信息
$wechatLogin = Db::name('device_wechat_login')
->where([
'deviceId' => $device['id'],
'companyId' => $this->getUserInfo('companyId'),
'alive' => 1
])
->order('createTime', 'desc')
->find();
$device['current_wechat'] = $wechatLogin ? [
'wechatId' => $wechatLogin['wechatId'],
'nickname' => $wechatLogin['nickname'] ?? '',
'loginTime' => date('Y-m-d H:i:s', $wechatLogin['createTime'])
] : null;
// 获取设备在该计划中的任务统计
$device['task_stats'] = $this->getDeviceTaskStats($device['id'], $planId);
// 移除原始extra字段
unset($device['extra']);
}
unset($device);
return ResponseHelper::success([
'total' => $total,
'list' => $list,
'plan_info' => [
'id' => $plan['id'],
'name' => $plan['name'],
'status' => $plan['status']
]
], '获取计划设备列表成功');
} catch (\Exception $e) {
return ResponseHelper::error('系统错误: ' . $e->getMessage(), 500);
}
}
/**
* 获取设备状态文本
*
* @param int $status
* @return string
*/
private function getDeviceStatusText($status)
{
$statusMap = [
0 => '离线',
1 => '在线',
2 => '忙碌',
3 => '故障'
];
return isset($statusMap[$status]) ? $statusMap[$status] : '未知';
}
/**
* 获取计划设备状态文本
*
* @param int $status
* @return string
*/
private function getPlanDeviceStatusText($status)
{
$statusMap = [
0 => '待分配',
1 => '已分配',
2 => '执行中',
3 => '已完成',
4 => '已暂停',
5 => '已取消'
];
return isset($statusMap[$status]) ? $statusMap[$status] : '未知';
}
/**
* 获取设备在指定计划中的任务统计
*
* @param int $deviceId
* @param int $planId
* @return array
*/
private function getDeviceTaskStats($deviceId, $planId)
{
// 获取该设备在计划中的任务总数
$totalTasks = Db::name('task_customer')
->where([
'task_id' => $planId,
'device_id' => $deviceId
])
->count();
// 获取已完成的任务数
$completedTasks = Db::name('task_customer')
->where([
'task_id' => $planId,
'device_id' => $deviceId,
'status' => 4
])
->count();
// 获取进行中的任务数
$processingTasks = Db::name('task_customer')
->where([
'task_id' => $planId,
'device_id' => $deviceId,
'status' => ['in', [1, 2, 3]]
])
->count();
return [
'total_tasks' => $totalTasks,
'completed_tasks' => $completedTasks,
'processing_tasks' => $processingTasks,
'completion_rate' => $totalTasks > 0 ? round(($completedTasks / $totalTasks) * 100, 2) : 0
];
}
public function getWxMinAppCode()
{
$params = $this->request->param();
$taskId = isset($params['taskId']) ? intval($params['taskId']) : 0;
if($taskId <= 0) {
return ResponseHelper::error('任务ID或场景ID不能为空', 400);
}
$task = Db::name('customer_acquisition_task')->where(['id' => $taskId, 'deleteTime' => 0])->find();
if(!$task) {
return ResponseHelper::error('任务不存在', 400);
}
$posterWeChatMiniProgram = new PosterWeChatMiniProgram();
$result = $posterWeChatMiniProgram->generateMiniProgramCodeWithScene($taskId);
return ResponseHelper::success($result, '获取小程序码成功');
}
}

View File

@@ -16,15 +16,14 @@ class PosterWeChatMiniProgram extends Controller
}
const MINI_PROGRAM_CONFIG = [
'app_id' => 'wx12345678',
'secret' => 'your-app-secret',
'app_id' => 'wx789850448e26c91d',
'secret' => 'd18f75b3a3623cb40da05648b08365a1',
'response_type' => 'array'
];
// 生成小程序码,存客宝-操盘手调用
public function generateMiniProgramCodeWithScene() {
public function generateMiniProgramCodeWithScene($taskId = '') {
$taskId = request()->param('id');
@@ -106,7 +105,7 @@ class PosterWeChatMiniProgram extends Controller
}
// return $result['phone_info']['phoneNumber'];
return json([
'code' => 0,
'code' => 200,
'message' => '获取手机号成功',
'data' => $result['phone_info']['phoneNumber']
]);
@@ -125,12 +124,45 @@ class PosterWeChatMiniProgram extends Controller
// todo 获取海报获客任务的任务/海报数据 -- 表还没设计好,不急 ck_customer_acquisition_task
public function getPosterTaskData() {
$id = request()->param('id');
$task = Db::name('customer_acquisition_task')->where('id', $id)->find();
$task = Db::name('customer_acquisition_task')->where(['id' => $id,'deleteTime' => 0])->find();
if (!$task) {
return json([
'code' => 400,
'message' => '任务不存在'
]);
}
if($task['status'] == 0) {
return json([
'code' => 400,
'message' => '任务已结束'
]);
}
$sceneConf = json_decode($task['sceneConf'], true);
if(isset($sceneConf['posters'][0]['preview'])) {
$posterUrl = $sceneConf['posters'][0]['preview'];
} else {
$posterUrl = '';
}
$data = [
'id' => $task['id'],
'name' => $task['name'],
'poster' => ['sUrl' => $posterUrl],
'sTip' => '啦啦啦啦',
];
// todo 只需 返回 poster_url success_tip
return json([
'code' => 0,
'code' => 200,
'message' => '获取海报获客任务数据成功',
'data' => $task
'data' => $data
]);
}