客服端 内容管理功能

This commit is contained in:
wong
2025-10-13 16:14:33 +08:00
parent 381eb37f1a
commit 6e2fc369e2
11 changed files with 963 additions and 26 deletions

View File

@@ -76,7 +76,6 @@ class MomentsController extends BaseController
'link' => $link,
'jobPublishWechatMomentsItems' => $jobPublishWechatMomentsItems
];
// 设置请求头
$headerData = ['client:system'];
$header = setHeader($headerData, $authorization, 'json');

View File

@@ -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');
});
});

View File

@@ -0,0 +1,431 @@
<?php
namespace app\chukebao\controller;
use app\chukebao\model\KfMoments;
use library\ResponseHelper;
use think\Db;
class MomentsController extends BaseController
{
/**
* 创建朋友圈
* @return \think\response\Json
*/
public function create()
{
$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); // 内容类型 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());
}
}
}

View File

@@ -0,0 +1,371 @@
<?php
namespace app\chukebao\controller;
use app\chukebao\model\Reply;
use app\chukebao\model\ReplyGroup;
use library\ResponseHelper;
class ReplyController extends BaseController
{
/**
* 列表
* @return \think\response\Json
* @throws \Exception
*/
public function getList()
{
$replyType = $this->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);
}
}
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace app\chukebao\model;
use think\Model;
class KfMoments extends Model
{
protected $table = 'ck_kf_moments';
}

View File

@@ -0,0 +1,17 @@
<?php
namespace app\chukebao\model;
use think\Model;
class Reply extends Model
{
protected $pk = 'id';
protected $name = 'kf_reply';
// 自动写入时间戳
protected $autoWriteTimestamp = true;
protected $createTime = 'createTime';
protected $updateTime = 'lastUpdateTime';
}

View File

@@ -0,0 +1,10 @@
<?php
namespace app\chukebao\model;
use think\Model;
class ReplyGroup extends Model
{
protected $pk = 'id';
protected $name = 'kf_reply_group';
}

View File

@@ -34,6 +34,7 @@ class WorkbenchMomentsCommand extends Command
// 检查队列是否已经在运行
$queueLockKey = "queue_lock:{$this->queueName}";
Cache::rm($queueLockKey);
if (Cache::get($queueLockKey)) {
$output->writeln("队列 {$this->queueName} 已经在运行中,跳过执行");
Log::warning("队列 {$this->queueName} 已经在运行中,跳过执行");

View File

@@ -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');

View File

@@ -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]);
}
}
}

View File

@@ -1,4 +1,5 @@
<?php
namespace app\job;
use app\api\controller\WebSocketController;
@@ -13,6 +14,7 @@ use think\queue\Job;
use think\facade\Cache;
use think\facade\Config;
use app\api\controller\MomentsController as Moments;
use app\chukebao\model\KfMoments;
/**
* 工作台朋友圈同步任务
@@ -52,6 +54,7 @@ class WorkbenchMomentsJob
$queueLockKey = $data['queueLockKey'] ?? '';
try {
$this->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;
}
}