From 6e2fc369e217d07cde69cafc7bf4c6e59ba86aba Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Mon, 13 Oct 2025 16:14:33 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=A2=E6=9C=8D=E7=AB=AF=20=E5=86=85?= =?UTF-8?q?=E5=AE=B9=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/controller/MomentsController.php | 1 - Server/application/chukebao/config/route.php | 20 + .../chukebao/controller/MomentsController.php | 431 ++++++++++++++++++ .../chukebao/controller/ReplyController.php | 371 +++++++++++++++ .../application/chukebao/model/KfMoments.php | 11 + Server/application/chukebao/model/Reply.php | 17 + .../application/chukebao/model/ReplyGroup.php | 10 + .../command/WorkbenchMomentsCommand.php | 1 + Server/application/cunkebao/config/route.php | 5 +- .../device/GetDeviceDetailV1Controller.php | 30 ++ .../application/job/WorkbenchMomentsJob.php | 92 +++- 11 files changed, 963 insertions(+), 26 deletions(-) create mode 100644 Server/application/chukebao/controller/MomentsController.php create mode 100644 Server/application/chukebao/controller/ReplyController.php create mode 100644 Server/application/chukebao/model/KfMoments.php create mode 100644 Server/application/chukebao/model/Reply.php create mode 100644 Server/application/chukebao/model/ReplyGroup.php diff --git a/Server/application/api/controller/MomentsController.php b/Server/application/api/controller/MomentsController.php index 38122d7b..715f68be 100644 --- a/Server/application/api/controller/MomentsController.php +++ b/Server/application/api/controller/MomentsController.php @@ -76,7 +76,6 @@ class MomentsController extends BaseController 'link' => $link, 'jobPublishWechatMomentsItems' => $jobPublishWechatMomentsItems ]; - // 设置请求头 $headerData = ['client:system']; $header = setHeader($headerData, $authorization, 'json'); diff --git a/Server/application/chukebao/config/route.php b/Server/application/chukebao/config/route.php index be698c67..1af6fa36 100644 --- a/Server/application/chukebao/config/route.php +++ b/Server/application/chukebao/config/route.php @@ -147,6 +147,26 @@ Route::group('v1/', function () { Route::put('readAll', 'app\chukebao\controller\NoticeController@readAll'); }); + Route::group('reply/', function () { + Route::get('list', 'app\chukebao\controller\ReplyController@getList'); + Route::post('addGroup', 'app\chukebao\controller\ReplyController@addGroup'); + Route::post('addReply', 'app\chukebao\controller\ReplyController@addReply'); + Route::post('updateGroup', 'app\chukebao\controller\ReplyController@updateGroup'); + Route::post('updateReply', 'app\chukebao\controller\ReplyController@updateReply'); + Route::delete('deleteGroup', 'app\chukebao\controller\ReplyController@deleteGroup'); + Route::delete('deleteReply', 'app\chukebao\controller\ReplyController@deleteReply'); + }); + + + Route::group('moments/', function () { + Route::post('add', 'app\chukebao\controller\MomentsController@create'); + Route::post('update', 'app\chukebao\controller\MomentsController@update'); + Route::delete('delete', 'app\chukebao\controller\MomentsController@delete'); + Route::get('list', 'app\chukebao\controller\MomentsController@getList'); + }); + + + }); diff --git a/Server/application/chukebao/controller/MomentsController.php b/Server/application/chukebao/controller/MomentsController.php new file mode 100644 index 00000000..2019a047 --- /dev/null +++ b/Server/application/chukebao/controller/MomentsController.php @@ -0,0 +1,431 @@ +getUserInfo('id'); + $companyId = $this->getUserInfo('companyId'); + + // 获取请求参数 + $text = $this->request->param('content', ''); // 朋友圈内容 + $picUrlList = $this->request->param('picUrlList', []); // 图片列表 + $videoUrl = $this->request->param('videoUrl', ''); // 视频链接 + $link = $this->request->param('link', []); // 链接信息 + $momentContentType = (int)$this->request->param('type', 1); // 内容类型 1文本 2图文 3视频 4链接 + $publicMode = (int)$this->request->param('publicMode', 0); // 公开模式 + $wechatIds = $this->request->param('wechatIds', []); // 微信账号ID列表 + $labels = $this->request->param('labels', []); // 标签列表 + $timingTime = $this->request->param('timingTime', date('Y-m-d H:i:s')); // 定时发布时间 + $immediately = $this->request->param('immediately', false); // 是否立即发布 + + // 参数验证 + if (empty($text) && empty($picUrlList) && empty($videoUrl)) { + return ResponseHelper::error('朋友圈内容不能为空'); + } + + if (empty($wechatIds)) { + return ResponseHelper::error('请选择发布账号'); + } + + // 校验内容类型 + if (!in_array($momentContentType, [1, 2, 3, 4])) { + return ResponseHelper::error('内容类型不合法,支持:1文本 2图文 3视频 4链接'); + } + + if(!empty($labels)){ + $publicMode = 2; + } + + // 根据内容类型校验必要参数 + switch ($momentContentType) { + case 1: // 文本 + if (empty($text)) { + return ResponseHelper::error('文本类型必须填写内容'); + } + break; + case 2: // 图文 + if (empty($text) || empty($picUrlList)) { + return ResponseHelper::error('图文类型必须填写内容和上传图片'); + } + break; + case 3: // 视频 + if (empty($videoUrl)) { + return ResponseHelper::error('视频类型必须上传视频'); + } + break; + case 4: // 链接 + if (empty($link)) { + return ResponseHelper::error('链接类型必须填写链接信息'); + } + if (empty($link['url'])) { + return ResponseHelper::error('链接类型必须填写链接地址'); + } + if (empty($link['desc'])) { + return ResponseHelper::error('链接类型必须填写链接描述'); + } + if (empty($link['image'])) { + return ResponseHelper::error('链接类型必须填写链接图片'); + } + break; + } + + // 处理链接信息 - 所有链接都必须验证 + if (!empty($link)) { + $link = [ + 'desc' => $link['desc'] ?? '', + 'image' => $link['image'] ?? '', + 'url' => $link['url'] ?? '' + ]; + + // 验证链接URL格式 + if (!empty($link['url']) && !filter_var($link['url'], FILTER_VALIDATE_URL)) { + return ResponseHelper::error('链接地址格式不正确'); + } + } else { + $link = ['desc' => '', 'image' => '', 'url' => '']; + } + + // 构建发布账号列表 + $jobPublishWechatMomentsItems = $this->buildJobPublishWechatMomentsItems($wechatIds, $labels); + if (empty($jobPublishWechatMomentsItems)) { + return ResponseHelper::error('无法获取有效的发布账号信息'); + } + + try { + // 构建发送数据 + $sendData = [ + 'altList' => '', + 'beginTime' => $timingTime, + 'endTime' => date('Y-m-d H:i:s', strtotime($timingTime) + 3600), + 'immediately' => $immediately, + 'isUseLocation' => false, + 'jobPublishWechatMomentsItems' => $jobPublishWechatMomentsItems, + 'lat' => 0, + 'lng' => 0, + 'link' => $link, + 'momentContentType' => $momentContentType, + 'picUrlList' => $picUrlList, + 'poiAddress' => '', + 'poiName' => '', + 'publicMode' => $publicMode, + 'text' => $text, + 'timingTime' => $timingTime ?: date('Y-m-d H:i:s'), + 'videoUrl' => $videoUrl + ]; + + // 保存到数据库 + $moments = new KfMoments(); + $moments->companyId = $companyId; + $moments->userId = $userId; + $moments->sendData = json_encode($sendData, 256); + $nowTs = time(); + $moments->createTime = $nowTs; + $moments->updateTime = $nowTs; + $moments->isDel = 0; + $moments->delTime = null; + $moments->isSend = $immediately ? 1 : 0; + $moments->sendTime = $immediately ? $nowTs : strtotime($timingTime); + $moments->save(); + + // 如果立即发布,调用发布接口 + if ($immediately) { + $this->publishMoments($sendData); + } + return ResponseHelper::success('', '朋友圈创建成功'); + } catch (\Exception $e) { + return ResponseHelper::error('创建失败:' . $e->getMessage()); + } + } + + /** + * 编辑朋友圈 + * @return \think\response\Json + */ + public function update() + { + $id = (int)$this->request->param('id', 0); + if ($id <= 0) { + return ResponseHelper::error('ID不合法'); + } + + $userId = $this->getUserInfo('id'); + $companyId = $this->getUserInfo('companyId'); + + // 获取请求参数(与创建一致的字段名) + $text = $this->request->param('content', ''); + $picUrlList = $this->request->param('picUrlList', []); + $videoUrl = $this->request->param('videoUrl', ''); + $link = $this->request->param('link', []); + $momentContentType = (int)$this->request->param('type', 1); + $publicMode = (int)$this->request->param('publicMode', 0); + $wechatIds = $this->request->param('wechatIds', []); + $labels = $this->request->param('labels', []); + $timingTime = $this->request->param('timingTime', date('Y-m-d H:i:s')); + $immediately = $this->request->param('immediately', false); + + // 读取待编辑记录 + /** @var KfMoments|null $moments */ + $moments = KfMoments::where(['id' => $id, 'companyId' => $companyId, 'userId' => $userId, 'isDel' => 0])->find(); + if (empty($moments)) { + return ResponseHelper::error('朋友圈不存在'); + } + + // 参数校验 + if (empty($text) && empty($picUrlList) && empty($videoUrl)) { + return ResponseHelper::error('朋友圈内容不能为空'); + } + if (empty($wechatIds)) { + return ResponseHelper::error('请选择发布账号'); + } + if (!in_array($momentContentType, [1, 2, 3, 4])) { + return ResponseHelper::error('内容类型不合法,支持:1文本 2图文 3视频 4链接'); + } + if (!empty($labels)) { + $publicMode = 2; + } + switch ($momentContentType) { + case 1: + if (empty($text)) { + return ResponseHelper::error('文本类型必须填写内容'); + } + break; + case 2: + if (empty($text) || empty($picUrlList)) { + return ResponseHelper::error('图文类型必须填写内容和上传图片'); + } + break; + case 3: + if (empty($videoUrl)) { + return ResponseHelper::error('视频类型必须上传视频'); + } + break; + case 4: + if (empty($link)) { + return ResponseHelper::error('链接类型必须填写链接信息'); + } + if (empty($link['url'])) { + return ResponseHelper::error('链接类型必须填写链接地址'); + } + if (empty($link['desc'])) { + return ResponseHelper::error('链接类型必须填写链接描述'); + } + if (empty($link['image'])) { + return ResponseHelper::error('链接类型必须填写链接图片'); + } + break; + } + if (!empty($link)) { + $link = [ + 'desc' => $link['desc'] ?? '', + 'image' => $link['image'] ?? '', + 'url' => $link['url'] ?? '' + ]; + if (!empty($link['url']) && !filter_var($link['url'], FILTER_VALIDATE_URL)) { + return ResponseHelper::error('链接地址格式不正确'); + } + } else { + $link = ['desc' => '', 'image' => '', 'url' => '']; + } + + // 构建账号列表 + $jobPublishWechatMomentsItems = $this->buildJobPublishWechatMomentsItems($wechatIds, $labels); + if (empty($jobPublishWechatMomentsItems)) { + return ResponseHelper::error('无法获取有效的发布账号信息'); + } + + try { + $sendData = [ + 'altList' => '', + 'beginTime' => $timingTime, + 'endTime' => date('Y-m-d H:i:s', strtotime($timingTime) + 1800), + 'immediately' => $immediately, + 'isUseLocation' => false, + 'jobPublishWechatMomentsItems' => $jobPublishWechatMomentsItems, + 'lat' => 0, + 'lng' => 0, + 'link' => $link, + 'momentContentType' => $momentContentType, + 'picUrlList' => $picUrlList, + 'poiAddress' => '', + 'poiName' => '', + 'publicMode' => $publicMode, + 'text' => $text, + 'timingTime' => $timingTime ?: date('Y-m-d H:i:s'), + 'videoUrl' => $videoUrl + ]; + + $moments->sendData = json_encode($sendData, 256); + $moments->isSend = $immediately ? 1 : 0; + $moments->sendTime = $immediately ? time() : strtotime($timingTime); + $moments->updateTime = time(); + $moments->save(); + + if ($immediately) { + $this->publishMoments($sendData); + } + + return ResponseHelper::success('', '朋友圈更新成功'); + } catch (\Exception $e) { + return ResponseHelper::error('更新失败:' . $e->getMessage()); + } + } + + /** + * 构建发布账号列表 + * @param array $wechatIds 微信账号ID列表 + * @param array $labels 标签列表 + * @return array + */ + private function buildJobPublishWechatMomentsItems($wechatIds, $labels) + { + try { + // 查询微信账号信息 + $wechatAccounts = Db::table('s2_wechat_account') + ->whereIn('id', $wechatIds) + ->field('id,labels') + ->select(); + if (empty($wechatAccounts)) { + return []; + } + + $result = []; + foreach ($wechatAccounts as $account) { + $accountLabels = []; + + // 如果账号有标签,解析标签 + if (!empty($account['labels'])) { + $accountLabels = is_string($account['labels']) + ? json_decode($account['labels'], true) + : $account['labels']; + } + + // 取传入标签与账号标签的交集 + $finalLabels = array_intersect($labels, $accountLabels); + + $result[] = [ + 'wechatAccountId' => $account['id'], + 'labels' => array_values($finalLabels), // 重新索引数组 + 'comments' => [] + ]; + } + + return $result; + + } catch (\Exception $e) { + \think\facade\Log::error('构建发布账号列表失败:' . $e->getMessage()); + return []; + } + } + + /** + * 发布朋友圈到微信 + * @param array $sendData + * @return bool + */ + private function publishMoments($sendData) + { + try { + // 这里调用实际的朋友圈发布接口 + // 根据您的系统架构,可能需要调用 WebSocket 或其他服务 + // 示例:调用 MomentsController 的 addJob 方法 + $moments = new \app\api\controller\MomentsController(); + return $moments->addJob($sendData); + } catch (\Exception $e) { + // 记录错误日志 + \think\facade\Log::error('朋友圈发布失败:' . $e->getMessage()); + return false; + } + } + + /** + * 获取朋友圈列表 + * @return \think\response\Json + */ + public function getList() + { + $page = (int)$this->request->param('page', 1); + $limit = (int)$this->request->param('limit', 10); + $userId = $this->getUserInfo('id'); + $companyId = $this->getUserInfo('companyId'); + + try { + $list = KfMoments::where(['companyId' => $companyId, 'userId' => $userId, 'isDel' => 0]) + ->order('createTime desc') + ->page($page, $limit) + ->select(); + + $total = KfMoments::where(['companyId' => $companyId, 'userId' => $userId, 'isDel' => 0])->count(); + + // 处理数据 + $data = []; + foreach ($list as $item) { + $sendData = json_encode($item->sendData,true); + $data[] = [ + 'id' => $item->id, + 'text' => $sendData['text'] ?? '', + 'momentContentType' => $sendData['momentContentType'] ?? 1, + 'picUrlList' => $sendData['picUrlList'] ?? [], + 'videoUrl' => $sendData['videoUrl'] ?? '', + 'link' => $sendData['link'] ?? [], + 'publicMode' => $sendData['publicMode'] ?? 2, + 'isSend' => $item->isSend, + 'createTime' => $item->createTime, + 'sendTime' => $item->sendTime, + 'accountCount' => count($sendData['jobPublishWechatMomentsItems'] ?? []) + ]; + } + + return ResponseHelper::success([ + 'list' => $data, + 'total' => $total, + 'page' => $page, + 'limit' => $limit + ], '获取成功'); + + } catch (\Exception $e) { + return ResponseHelper::error('获取失败:' . $e->getMessage()); + } + } + + /** + * 删除朋友圈 + * @return \think\response\Json + */ + public function delete() + { + $id = (int)$this->request->param('id', 0); + $userId = $this->getUserInfo('id'); + $companyId = $this->getUserInfo('companyId'); + + if ($id <= 0) { + return ResponseHelper::error('ID不合法'); + } + + try { + $moments = KfMoments::where(['id' => $id, 'companyId' => $companyId, 'userId' => $userId, 'isDel' => 0])->find(); + if (empty($moments)) { + return ResponseHelper::error('朋友圈不存在'); + } + + $moments->isDel = 1; + $moments->delTime = time(); + $moments->updateTime = time(); + $moments->save(); + return ResponseHelper::success([], '删除成功'); + + } catch (\Exception $e) { + return ResponseHelper::error('删除失败:' . $e->getMessage()); + } + } +} \ No newline at end of file diff --git a/Server/application/chukebao/controller/ReplyController.php b/Server/application/chukebao/controller/ReplyController.php new file mode 100644 index 00000000..fb1dea7f --- /dev/null +++ b/Server/application/chukebao/controller/ReplyController.php @@ -0,0 +1,371 @@ +request->param('replyType', 0); + $keyword = $this->request->param('keyword', ''); + $userId = $this->getUserInfo('id'); + $companyId = $this->getUserInfo('companyId'); + + try { + // 构建分组查询条件 + $groupWhere = [ + ['isDel','=',0] + ]; + switch ($replyType) { + case 0: + //公共快捷语 + $groupWhere[] = ['replyType', '=', 0]; + break; + case 1: + //私有快捷语 + $groupWhere[] = ['companyId', '=', $companyId]; + $groupWhere[] = ['userId', '=', $userId]; + $groupWhere[] = ['replyType', '=', 1]; + break; + case 2: + //公司快捷语 + $groupWhere[] = ['companyId', '=', $companyId]; + $groupWhere[] = ['replyType', '=', 2]; + break; + default: + $groupWhere[] = ['replyType', '=', 0]; + break; + } + + + if (!empty($keyword)) { + $groupWhere[] = ['groupName','like', '%' . $keyword . '%']; + } + + // 获取所有分组 + $allGroups = ReplyGroup::where($groupWhere) + ->order('sortIndex asc,id DESC') + ->select(); + // 构建树形结构 + $result = $this->buildGroupTree($allGroups, $keyword); + + return ResponseHelper::success($result, '获取成功'); + + } catch (\Exception $e) { + return ResponseHelper::error('获取失败:' . $e->getMessage()); + } + } + + /** + * 新增快捷语分组 + * @return \think\response\Json + */ + public function addGroup() + { + $groupName = $this->request->param('groupName', ''); + $parentId = (int)$this->request->param('parentId', 0); + $replyType = (int)$this->request->param('replyType', 0); // 0公共 1私有 2公司 + $sortIndex = (string)$this->request->param('sortIndex', 50); + + $userId = $this->getUserInfo('id'); + $companyId = $this->getUserInfo('companyId'); + $accountId = $this->getUserInfo('s2_accountId'); + + if ($groupName === '') { + return ResponseHelper::error('分组名称不能为空'); + } + + try { + $data = [ + 'groupName' => $groupName, + 'parentId' => $parentId, + 'replyType' => $replyType, + 'sortIndex' => $sortIndex, + // 兼容现有程序中使用到的字段 + 'companyId' => $companyId, + 'userId' => $userId, + ]; + + /** @var ReplyGroup $group */ + $group = new ReplyGroup(); + $group->save($data); + + return ResponseHelper::success($group->toArray(), '创建成功'); + } catch (\Exception $e) { + return ResponseHelper::error('创建失败:' . $e->getMessage()); + } + } + + /** + * 新增快捷语 + * @return \think\response\Json + */ + public function addReply() + { + $groupId = (int)$this->request->param('groupId', 0); + $title = $this->request->param('title', ''); + $msgType = (int)$this->request->param('msgType', 1); // 1文本 3图片 43视频 49链接 等 + $content = $this->request->param('content', ''); + $sortIndex = (string)$this->request->param('sortIndex', 50); + + $accountId = $this->getUserInfo('s2_accountId'); + $companyId = $this->getUserInfo('companyId'); + $userId = $this->getUserInfo('id'); + + if ($groupId <= 0) { + return ResponseHelper::error('分组ID不合法'); + } + if ($title === '') { + return ResponseHelper::error('标题不能为空'); + } + + try { + $now = time(); + $data = [ + 'tenantId' => $companyId, + 'groupId' => $groupId, + 'accountId' => $accountId, + 'title' => $title, + 'msgType' => $msgType, + 'content' => $content, + 'sortIndex' => $sortIndex, + 'createTime' => $now, + 'lastUpdateTime' => $now, + 'userId' => $userId, + ]; + + /** @var Reply $reply */ + $reply = new Reply(); + $reply->save($data); + + return ResponseHelper::success($reply->toArray(), '创建成功'); + } catch (\Exception $e) { + return ResponseHelper::error('创建失败:' . $e->getMessage()); + } + } + + /** + * 编辑快捷语分组 + * @return \think\response\Json + */ + public function updateGroup() + { + $id = (int)$this->request->param('id', 0); + if ($id <= 0) { + return ResponseHelper::error('分组ID不合法'); + } + + $data = []; + $groupName = $this->request->param('groupName', null); + $parentId = $this->request->param('parentId', null); + $replyType = $this->request->param('replyType', null); + $sortIndex = $this->request->param('sortIndex', null); + + if ($groupName !== null) $data['groupName'] = $groupName; + if ($parentId !== null) $data['parentId'] = (int)$parentId; + if ($replyType !== null) $data['replyType'] = (int)$replyType; + if ($sortIndex !== null) $data['sortIndex'] = (string)$sortIndex; + + if (empty($data)) { + return ResponseHelper::error('无可更新字段'); + } + + try { + $group = ReplyGroup::where(['id' => $id,'isDel' => 0])->find(); + if (empty($group)) { + return ResponseHelper::error('分组不存在'); + } + $group->save($data); + return ResponseHelper::success($group->toArray(), '更新成功'); + } catch (\Exception $e) { + return ResponseHelper::error('更新失败:' . $e->getMessage()); + } + } + + /** + * 假删除快捷语分组 + * @return \think\response\Json + */ + public function deleteGroup() + { + $id = (int)$this->request->param('id', 0); + if ($id <= 0) { + return ResponseHelper::error('分组ID不合法'); + } + try { + $group = ReplyGroup::where(['id' => $id,'isDel' => 0])->find(); + if (empty($group)) { + return ResponseHelper::error('分组不存在'); + } + $group->save(['isDel' => 1,'delTime' => time()]); + return ResponseHelper::success([], '删除成功'); + } catch (\Exception $e) { + return ResponseHelper::error('删除失败:' . $e->getMessage()); + } + } + + /** + * 编辑快捷语 + * @return \think\response\Json + */ + public function updateReply() + { + $id = (int)$this->request->param('id', 0); + if ($id <= 0) { + return ResponseHelper::error('快捷语ID不合法'); + } + + $data = []; + $groupId = $this->request->param('groupId', null); + $title = $this->request->param('title', null); + $msgType = $this->request->param('msgType', null); + $content = $this->request->param('content', null); + $sortIndex = $this->request->param('sortIndex', null); + + if ($groupId !== null) $data['groupId'] = (int)$groupId; + if ($title !== null) $data['title'] = $title; + if ($msgType !== null) $data['msgType'] = (int)$msgType; + if ($content !== null) $data['content'] = $content; + if ($sortIndex !== null) $data['sortIndex'] = (string)$sortIndex; + if (!empty($data)) { + $data['lastUpdateTime'] = time(); + } + + if (empty($data)) { + return ResponseHelper::error('无可更新字段'); + } + + try { + $reply = Reply::where(['id' => $id,'isDel' => 0])->find(); + if (empty($reply)) { + return ResponseHelper::error('快捷语不存在'); + } + $reply->save($data); + return ResponseHelper::success($reply->toArray(), '更新成功'); + } catch (\Exception $e) { + return ResponseHelper::error('更新失败:' . $e->getMessage()); + } + } + + /** + * 假删除快捷语 + * @return \think\response\Json + */ + public function deleteReply() + { + $id = (int)$this->request->param('id', 0); + if ($id <= 0) { + return ResponseHelper::error('快捷语ID不合法'); + } + try { + $reply = Reply::where(['id' => $id,'isDel' => 0])->find(); + if (empty($reply)) { + return ResponseHelper::error('快捷语不存在'); + } + $reply->save(['isDel' => 1, 'delTime' => time()]); + return ResponseHelper::success([], '删除成功'); + } catch (\Exception $e) { + return ResponseHelper::error('删除失败:' . $e->getMessage()); + } + } + + /** + * 构建分组树形结构 + * @param array $groups 所有分组数据 + * @param string $keyword 搜索关键词 + * @return array + */ + private function buildGroupTree($groups, $keyword = '') + { + $tree = []; + $groupMap = []; + + // 先构建分组映射 + foreach ($groups as $group) { + $groupMap[$group->id] = $group->toArray(); + } + + // 构建树形结构 + foreach ($groups as $group) { + $groupData = $this->buildGroupData($group, $keyword); + + if ($group->parentId == null || $group->parentId == 0) { + // 顶级分组 + $tree[] = $groupData; + } else { + // 子分组,需要找到父分组并添加到其children中 + $this->addToParentGroup($tree, $group->parentId, $groupData); + } + } + + return $tree; + } + + /** + * 构建单个分组数据 + * @param object $group 分组对象 + * @param string $keyword 搜索关键词 + * @return array + */ + private function buildGroupData($group, $keyword = '') + { + // 构建快捷回复查询条件 + $replyWhere[] =[ + ['groupId' ,'=', $group->id], + ['isDel','=',0] + ]; + if (!empty($keyword)) { + $replyWhere[] = ['title','like', '%' . $keyword . '%']; + } + + // 获取该分组下的快捷回复 + $replies = Reply::where($replyWhere) + ->order('sortIndex asc, id desc + ') + ->select(); + + return [ + 'id' => $group->id, + 'groupName' => $group->groupName, + 'sortIndex' => $group->sortIndex, + 'parentId' => $group->parentId, + 'replyType' => $group->replyType, + 'replys' => $group->replys, + 'companyId' => $group->companyId, + 'userId' => $group->userId, + 'replies' => $replies->toArray(), + 'children' => [] // 子分组 + ]; + } + + /** + * 将子分组添加到父分组中 + * @param array $tree 树形结构 + * @param int $parentId 父分组ID + * @param array $childGroup 子分组数据 + */ + private function addToParentGroup(&$tree, $parentId, $childGroup) + { + foreach ($tree as &$group) { + if ($group['id'] == $parentId) { + $group['children'][] = $childGroup; + return; + } + + // 递归查找子分组 + if (!empty($group['children'])) { + $this->addToParentGroup($group['children'], $parentId, $childGroup); + } + } + } + +} \ No newline at end of file diff --git a/Server/application/chukebao/model/KfMoments.php b/Server/application/chukebao/model/KfMoments.php new file mode 100644 index 00000000..3e8e5012 --- /dev/null +++ b/Server/application/chukebao/model/KfMoments.php @@ -0,0 +1,11 @@ +queueName}"; + Cache::rm($queueLockKey); if (Cache::get($queueLockKey)) { $output->writeln("队列 {$this->queueName} 已经在运行中,跳过执行"); Log::warning("队列 {$this->queueName} 已经在运行中,跳过执行"); diff --git a/Server/application/cunkebao/config/route.php b/Server/application/cunkebao/config/route.php index 22e08ca9..1843f07b 100644 --- a/Server/application/cunkebao/config/route.php +++ b/Server/application/cunkebao/config/route.php @@ -17,6 +17,7 @@ Route::group('v1/', function () { // 设备管理相关 Route::group('devices', function () { + Route::get('isUpdataWechat', 'app\cunkebao\controller\device\GetDeviceDetailV1Controller@isUpdataWechat'); Route::put('refresh', 'app\cunkebao\controller\device\RefreshDeviceDetailV1Controller@index'); Route::get('add-results', 'app\cunkebao\controller\device\GetAddResultedV1Controller@index'); Route::post('task-config', 'app\cunkebao\controller\device\UpdateDeviceTaskConfigV1Controller@index'); @@ -38,8 +39,6 @@ Route::group('v1/', function () { Route::get(':wechatId', 'app\cunkebao\controller\wechat\GetWechatProfileV1Controller@index'); Route::post('transfer-friends', 'app\cunkebao\controller\wechat\PostTransferFriends@index'); // 微信好友转移 - - Route::get('count', 'app\cunkebao\controller\DeviceWechat@count'); Route::get('device-count', 'app\cunkebao\controller\DeviceWechat@deviceCount'); // 获取有登录微信的设备数量 Route::put('refresh', 'app\cunkebao\controller\DeviceWechat@refresh'); // 刷新设备微信状态 @@ -71,8 +70,6 @@ Route::group('v1/', function () { Route::post('addPackage', 'app\cunkebao\controller\traffic\GetPotentialListWithInCompanyV1Controller@addPackage'); - - Route::get('converted', 'app\cunkebao\controller\traffic\GetConvertedListWithInCompanyV1Controller@index'); Route::get('types', 'app\cunkebao\controller\traffic\GetPotentialTypeSectionV1Controller@index'); Route::get('sources', 'app\cunkebao\controller\traffic\GetTrafficSourceSectionV1Controller@index'); diff --git a/Server/application/cunkebao/controller/device/GetDeviceDetailV1Controller.php b/Server/application/cunkebao/controller/device/GetDeviceDetailV1Controller.php index 85e9823f..d61bad56 100644 --- a/Server/application/cunkebao/controller/device/GetDeviceDetailV1Controller.php +++ b/Server/application/cunkebao/controller/device/GetDeviceDetailV1Controller.php @@ -155,4 +155,34 @@ class GetDeviceDetailV1Controller extends BaseController return ResponseHelper::error($e->getMessage(), $e->getCode()); } } + + + public function isUpdataWechat() + { + $id = $this->request->param('id/d'); + $companyId = $this->getUserInfo('companyId'); + $newWechat = DeviceWechatLoginModel::alias('a') + ->field('b.*') + ->join('wechat_account b', 'a.wechatId = b.wechatId') + ->where(['a.deviceId' => $id,'a.isTips' => 0,'a.companyId' => $companyId]) + ->order('a.id', 'desc') + ->find(); + if (empty($newWechat)){ + return ResponseHelper::success('','该设备绑定的微信无需迁移',201); + } + + $oldWechat = DeviceWechatLoginModel::alias('a') + ->field('b.*') + ->join('wechat_account b', 'a.wechatId = b.wechatId') + ->where(['a.companyId' => $companyId]) + ->where('a.deviceId' ,'<>', $id) + ->order('a.id', 'desc') + ->find(); + if (empty($oldWechat)){ + return ResponseHelper::success('','该设备绑定的微信无需迁移',201); + }else{ + DeviceWechatLoginModel::where(['deviceId' => $id,'isTips' => 0,'companyId' => $companyId])->update(['isTips' => 1]);; + return ResponseHelper::success(['newWechat' => $newWechat,'oldWechat' => $oldWechat]); + } + } } \ No newline at end of file diff --git a/Server/application/job/WorkbenchMomentsJob.php b/Server/application/job/WorkbenchMomentsJob.php index 7aa26614..d8c008ac 100644 --- a/Server/application/job/WorkbenchMomentsJob.php +++ b/Server/application/job/WorkbenchMomentsJob.php @@ -1,4 +1,5 @@ logJobStart($jobId, $queueLockKey); + $this->execute2(); $this->execute(); $this->handleJobSuccess($job, $queueLockKey); return true; @@ -75,13 +78,13 @@ class WorkbenchMomentsJob if (!$config) { continue; } - $startTime = strtotime(date('Y-m-d '. $config['startTime'])); - $endTime = strtotime(date('Y-m-d '. $config['endTime'])); + $startTime = strtotime(date('Y-m-d ' . $config['startTime'])); + $endTime = strtotime(date('Y-m-d ' . $config['endTime'])); // 如果时间不符,则跳过 - if($startTime > time() || $endTime < time()){ + if ($startTime > time() || $endTime < time()) { continue; } - + // 获取设备 $devices = $this->getDevice($workbench, $config); if (empty($devices)) { @@ -102,6 +105,53 @@ class WorkbenchMomentsJob } } + public function execute2() + { + try { + // 获取所有工作台 + $kfMoments = KfMoments::where(['isSend' => 0, 'isDel' => 0])->where('sendTime', '<=', time() + 120)->order('id desc')->select(); + foreach ($kfMoments as $val) { + $sendData = json_decode($val->sendData,true); + $endTime = strtotime($sendData['endTime']); + if ($endTime <= time() + 1800){ + $endTime = time() + 3600; + $sendData['endTime'] = date('Y-m-d H:i:s', $endTime); + } + switch ($sendData['momentContentType']) { + case 1: + $sendData['link'] = ['image' => '']; + $sendData['picUrlList'] = []; + $sendData['videoUrl'] = ''; + break; + case 2: + $sendData['link'] = ['image' => '']; + $sendData['videoUrl'] = ''; + break; + case 3: + $sendData['link'] = ['image' => '']; + $sendData['picUrlList'] = []; + break; + case 4: + $sendData['picUrlList'] = []; + $sendData['videoUrl'] = ''; + break; + default: + $sendData['link'] = ['image' => '']; + $sendData['picUrlList'] = []; + $sendData['videoUrl'] = ''; + break; + } + $moments = new Moments(); + $moments->addJob($sendData); + KfMoments::where(['id' => $val['id']])->update(['isSend' => 1]); + } + } catch (\Exception $e) { + Log::error("朋友圈同步任务异常: " . $e->getMessage()); + throw $e; + } + } + + /** * 处理内容发送 * @param Workbench $workbench @@ -132,30 +182,30 @@ class WorkbenchMomentsJob $sendTime = !empty($contentLibrary['sendTime']) ? $contentLibrary['sendTime'] : time(); // 图片url - if($momentContentType == 2){ + if ($momentContentType == 2) { $picUrlList = json_decode($contentLibrary['resUrls'], true); - }else{ + } else { $picUrlList = []; } // 视频url - if($momentContentType == 3){ + if ($momentContentType == 3) { $videoUrl = json_decode($contentLibrary['urls'], true); $videoUrl = $videoUrl[0] ?? ''; - }else{ + } else { $videoUrl = ''; } // 链接url - if($momentContentType == 4){ - $urls = json_decode($contentLibrary['urls'],true); + if ($momentContentType == 4) { + $urls = json_decode($contentLibrary['urls'], true); $url = $urls[0] ?? []; $link = [ 'desc' => $url['desc'] ?? '', 'image' => $url['image'] ?? '', 'url' => $url['url'] ?? '' ]; - }else{ + } else { $link = ['image' => '']; } @@ -208,7 +258,7 @@ class WorkbenchMomentsJob ]; Db::name('workbench_moments_sync_item')->insert($data); } - + } /** @@ -303,7 +353,7 @@ class WorkbenchMomentsJob 'ci.comment', 'ci.sendTime' ]); - // 复制 query + // 复制 query $query2 = clone $query; $query3 = clone $query; // 根据accountType处理不同的发送逻辑 @@ -322,7 +372,7 @@ class WorkbenchMomentsJob // 获取下一个要发送的内容(从内容库中查询,排除isLoop为0的数据) $isPushIds = Db::name('workbench_moments_sync_item') - ->where(['workbenchId' => $workbench->id,'isLoop' => 0]) + ->where(['workbenchId' => $workbench->id, 'isLoop' => 0]) ->column('contentId'); if (empty($isPushIds)) { @@ -342,7 +392,7 @@ class WorkbenchMomentsJob ->update(['isLoop' => 1]); return false; } - + return $sentContent; } else { // 不能循环发送,只获取未发送的内容 @@ -362,9 +412,9 @@ class WorkbenchMomentsJob protected function logJobStart($jobId, $queueLockKey) { Log::info('开始处理工作台朋友圈同步任务: ' . json_encode([ - 'jobId' => $jobId, - 'queueLockKey' => $queueLockKey - ])); + 'jobId' => $jobId, + 'queueLockKey' => $queueLockKey + ])); } /** @@ -389,18 +439,18 @@ class WorkbenchMomentsJob protected function handleJobError(\Exception $e, $job, $queueLockKey) { Log::error('工作台朋友圈同步任务异常:' . $e->getMessage()); - + if (!empty($queueLockKey)) { Cache::rm($queueLockKey); Log::info("由于异常释放队列锁: {$queueLockKey}"); } - + if ($job->attempts() > self::MAX_RETRY_ATTEMPTS) { $job->delete(); } else { $job->release(Config::get('queue.failed_delay', 10)); } - + return false; } } \ No newline at end of file