入群欢迎语功能提交

This commit is contained in:
wong
2026-01-09 17:05:17 +08:00
parent 2b128195bf
commit 66d217d5f1
33 changed files with 3827 additions and 37 deletions

View File

@@ -805,11 +805,8 @@ class WebSocketController extends BaseController
"wechatChatroomId" => 0,
"wechatFriendId" => $dataArray['wechatFriendId'],
];
// 发送请求
$this->client->send(json_encode($params));
// 接收响应
$response = $this->client->receive();
$message = json_decode($response, true);
// 发送请求并获取响应
$message = $this->sendMessage($params);
if (!empty($message)) {
return json_encode(['code' => 200, 'msg' => '信息发送成功', 'data' => $message]);
}
@@ -853,12 +850,8 @@ class WebSocketController extends BaseController
"wechatChatroomId" => $dataArray['wechatChatroomId'],
"wechatFriendId" => 0,
];
// 发送请求
$this->client->send(json_encode($params));
// 接收响应
$response = $this->client->receive();
$message = json_decode($response, true);
// 发送请求并获取响应
$message = $this->sendMessage($params);
if (!empty($message)) {
return json_encode(['code' => 200, 'msg' => '信息发送成功', 'data' => $message]);
}
@@ -904,7 +897,7 @@ class WebSocketController extends BaseController
$message = [];
try {
//消息拼接 msgType(1:文本 3:图片 43:视频 47:动图表情包 49:小程序)
$result = [
$params = [
"cmdType" => "CmdSendMessage",
"content" => $dataArray['content'],
"msgSubType" => 0,
@@ -914,15 +907,10 @@ class WebSocketController extends BaseController
"wechatChatroomId" => $dataArray['wechatChatroomId'],
"wechatFriendId" => 0,
];
$result = json_encode($result);
$this->client->send($result);
$message = $this->client->receive();
//关闭WS链接
$this->client->close();
// 发送请求并获取响应
$message = $this->sendMessage($params);
//Log::write('WS群消息发送');
//Log::write($message);
$message = json_decode($message, 1);
} catch (\Exception $e) {
$msg = $e->getMessage();
}

View File

@@ -5,7 +5,9 @@ namespace app\api\controller;
use app\api\model\WechatChatroomModel;
use app\api\model\WechatChatroomMemberModel;
use app\job\WechatChatroomJob;
use app\job\WorkbenchGroupWelcomeJob;
use think\facade\Request;
use think\Queue;
class WechatChatroomController extends BaseController
{
@@ -218,8 +220,9 @@ class WechatChatroomController extends BaseController
])->find();
if ($member) {
$member->savea($data);
$member->save($data);
} else {
// 新成员,记录首次出现时间
$data['createTime'] = time();
WechatChatroomMemberModel::create($data);
}

View File

@@ -38,6 +38,7 @@ return [
'workbench:trafficDistribute' => 'app\command\WorkbenchTrafficDistributeCommand', // 工作台流量分发任务
'workbench:groupPush' => 'app\command\WorkbenchGroupPushCommand', // 工作台群推送任务
'workbench:groupCreate' => 'app\command\WorkbenchGroupCreateCommand', // 工作台群创建任务
'workbench:groupWelcome' => 'app\command\WorkbenchGroupWelcomeCommand', // 工作台入群欢迎语任务
'workbench:import-contact' => 'app\command\WorkbenchImportContactCommand', // 工作台通讯录导入任务
'kf:notice' => 'app\command\KfNoticeCommand', // 客服端消息通知

View File

@@ -0,0 +1,42 @@
<?php
namespace app\command;
use app\job\WorkbenchGroupWelcomeJob;
use think\console\Command;
use think\console\Input;
use think\console\Output;
use think\facade\Log;
class WorkbenchGroupWelcomeCommand extends Command
{
protected function configure()
{
$this->setName('workbench:groupWelcome')
->setDescription('工作台入群欢迎语任务队列');
}
protected function execute(Input $input, Output $output)
{
$output->writeln('开始处理工作台入群欢迎语任务...');
try {
$job = new WorkbenchGroupWelcomeJob();
$result = $job->processWelcomeMessage([], 0);
if ($result) {
$output->writeln('入群欢迎语任务处理完成');
} else {
$output->writeln('入群欢迎语任务处理失败');
}
return $result;
} catch (\Exception $e) {
$errorMsg = '工作台入群欢迎语任务执行失败:' . $e->getMessage();
Log::error($errorMsg);
$output->writeln($errorMsg);
return false;
}
}
}

View File

@@ -12,6 +12,7 @@ use app\cunkebao\model\WorkbenchImportContact;
use app\cunkebao\model\WorkbenchMomentsSync;
use app\cunkebao\model\WorkbenchGroupPush;
use app\cunkebao\model\WorkbenchGroupCreate;
use app\cunkebao\model\WorkbenchGroupWelcome;
use app\cunkebao\validate\Workbench as WorkbenchValidate;
use think\Controller;
use think\Db;
@@ -33,6 +34,7 @@ class WorkbenchController extends Controller
const TYPE_GROUP_CREATE = 4; // 自动建群
const TYPE_TRAFFIC_DISTRIBUTION = 5; // 流量分发
const TYPE_IMPORT_CONTACT = 6; // 联系人导入
const TYPE_GROUP_WELCOME = 7; // 入群欢迎语
/**
* 创建工作台
@@ -49,7 +51,6 @@ class WorkbenchController extends Controller
// 获取请求参数
$param = $this->request->post();
// 根据业务默认值补全参数
if (
@@ -201,6 +202,30 @@ class WorkbenchController extends Controller
$config->createTime = time();
$config->save();
break;
case self::TYPE_GROUP_WELCOME: // 入群欢迎语
$config = new WorkbenchGroupWelcome;
$config->workbenchId = $workbench->id;
$config->devices = json_encode($param['deviceGroups'] ?? [], JSON_UNESCAPED_UNICODE);
$config->groups = json_encode($param['wechatGroups'] ?? [], JSON_UNESCAPED_UNICODE);
$config->startTime = $param['startTime'] ?? '';
$config->endTime = $param['endTime'] ?? '';
$config->interval = isset($param['interval']) ? intval($param['interval']) : 0;
// messages 作为 JSON 存储(如果表中有 messages 字段)
if (isset($param['messages']) && is_array($param['messages'])) {
// 按 order 排序
usort($param['messages'], function($a, $b) {
$orderA = isset($a['order']) ? intval($a['order']) : 0;
$orderB = isset($b['order']) ? intval($b['order']) : 0;
return $orderA <=> $orderB;
});
$config->messages = json_encode($param['messages'], JSON_UNESCAPED_UNICODE);
} else {
$config->messages = json_encode([], JSON_UNESCAPED_UNICODE);
}
$config->createTime = time();
$config->updateTime = time();
$config->save();
break;
}
Db::commit();
@@ -456,6 +481,23 @@ class WorkbenchController extends Controller
}
unset($item->importContact, $item->import_contact);
break;
case self::TYPE_GROUP_WELCOME:
if (!empty($item->groupWelcome)) {
$item->config = $item->groupWelcome;
$item->config->deviceGroups = json_decode($item->config->devices, true);
$item->config->wechatGroups = json_decode($item->config->groups, true);
// 解析 messages JSON 字段
if (!empty($item->config->messages)) {
$item->config->messages = json_decode($item->config->messages, true);
if (!is_array($item->config->messages)) {
$item->config->messages = [];
}
} else {
$item->config->messages = [];
}
}
unset($item->groupWelcome, $item->group_welcome);
break;
}
// 添加创建人名称
$item['creatorName'] = $item->user ? $item->user->username : '';
@@ -510,6 +552,9 @@ class WorkbenchController extends Controller
'importContact' => function ($query) {
$query->field('workbenchId,devices,pools,num,remarkType,remark,clearContact,startTime,endTime');
},
'groupWelcome' => function ($query) {
$query->field('workbenchId,devices,groups,startTime,endTime,interval,messages');
},
];
$where = [
@@ -773,6 +818,23 @@ class WorkbenchController extends Controller
}
unset($workbench->importContact, $workbench->import_contact);
break;
case self::TYPE_GROUP_WELCOME:
if (!empty($workbench->groupWelcome)) {
$workbench->config = $workbench->groupWelcome;
$workbench->config->deviceGroups = json_decode($workbench->config->devices, true);
$workbench->config->wechatGroups = json_decode($workbench->config->groups, true);
// 解析 messages JSON 字段
if (!empty($workbench->config->messages)) {
$workbench->config->messages = json_decode($workbench->config->messages, true);
if (!is_array($workbench->config->messages)) {
$workbench->config->messages = [];
}
} else {
$workbench->config->messages = [];
}
}
unset($workbench->groupWelcome, $workbench->group_welcome);
break;
}
unset(
$workbench->autoLike,
@@ -873,13 +935,14 @@ class WorkbenchController extends Controller
}
// 获取群当targetType=1时
if (!empty($workbench->config->wechatGroups) && isset($workbench->config->targetType) && $workbench->config->targetType == 1) {
$groupList = Db::name('wechat_group')->alias('wg')
->join('wechat_account wa', 'wa.wechatId = wg.ownerWechatId')
->where('wg.id', 'in', $workbench->config->wechatGroups)
->order('wg.id', 'desc')
->field('wg.id,wg.name as groupName,wg.ownerWechatId,wa.nickName,wa.avatar,wa.alias,wg.avatar as groupAvatar')
if (!empty($workbench->config->wechatGroups) && $workbench->type != self::TYPE_GROUP_CREATE) {
$groupList = Db::table('s2_wechat_chatroom')->alias('wc')
->whereIn('wc.id', $workbench->config->wechatGroups)
->where('wc.isDeleted', 0)
->order('wc.id', 'desc')
->field('wc.id,wc.nickname as groupName,wc.wechatAccountWechatId as ownerWechatId,wc.wechatAccountNickname as nickName,wc.wechatAccountAvatar as avatar,wc.wechatAccountAlias as alias,wc.chatroomAvatar as groupAvatar')
->select();
$workbench->config->wechatGroupsOptions = $groupList;
} else {
@@ -887,7 +950,7 @@ class WorkbenchController extends Controller
}
// 获取好友当targetType=2时
if (!empty($workbench->config->wechatFriends) && isset($workbench->config->targetType) && $workbench->config->targetType == 2) {
if (!empty($workbench->config->wechatFriends)) {
$friendList = Db::table('s2_wechat_friend')->alias('wf')
->join(['s2_wechat_account' => 'wa'], 'wa.id = wf.wechatAccountId', 'left')
->where('wf.id', 'in', $workbench->config->wechatFriends)
@@ -900,7 +963,7 @@ class WorkbenchController extends Controller
}
// 获取流量池当targetType=2时
if (!empty($workbench->config->trafficPools) && isset($workbench->config->targetType) && $workbench->config->targetType == 2) {
if (!empty($workbench->config->trafficPools)) {
$poolList = [];
$companyId = $this->request->userInfo['companyId'];
@@ -1065,9 +1128,7 @@ class WorkbenchController extends Controller
}
$workbench->config->wechatGroupsOptions = $wechatGroupsOptions;
} else {
$workbench->config->wechatGroupsOptions = [];
}
}
// 获取管理员选项(自动建群)
if ($workbench->type == self::TYPE_GROUP_CREATE && !empty($workbench->config->admins)) {
@@ -1258,6 +1319,30 @@ class WorkbenchController extends Controller
$config->save();
}
break;
case self::TYPE_GROUP_WELCOME: // 入群欢迎语
$config = WorkbenchGroupWelcome::where('workbenchId', $param['id'])->find();
if ($config) {
$config->devices = json_encode($param['deviceGroups'] ?? [], JSON_UNESCAPED_UNICODE);
$config->groups = json_encode($param['wechatGroups'] ?? [], JSON_UNESCAPED_UNICODE);
$config->startTime = $param['startTime'] ?? '';
$config->endTime = $param['endTime'] ?? '';
$config->interval = isset($param['interval']) ? intval($param['interval']) : 0;
// messages 作为 JSON 存储
if (isset($param['messages']) && is_array($param['messages'])) {
// 按 order 排序
usort($param['messages'], function($a, $b) {
$orderA = isset($a['order']) ? intval($a['order']) : 0;
$orderB = isset($b['order']) ? intval($b['order']) : 0;
return $orderA <=> $orderB;
});
$config->messages = json_encode($param['messages'], JSON_UNESCAPED_UNICODE);
} else {
$config->messages = json_encode([], JSON_UNESCAPED_UNICODE);
}
$config->updateTime = time();
$config->save();
}
break;
}
Db::commit();
@@ -1476,6 +1561,22 @@ class WorkbenchController extends Controller
$newConfig->save();
}
break;
case self::TYPE_GROUP_WELCOME: // 入群欢迎语
$config = WorkbenchGroupWelcome::where('workbenchId', $id)->find();
if ($config) {
$newConfig = new WorkbenchGroupWelcome;
$newConfig->workbenchId = $newWorkbench->id;
$newConfig->devices = $config->devices;
$newConfig->groups = $config->groups;
$newConfig->startTime = $config->startTime;
$newConfig->endTime = $config->endTime;
$newConfig->interval = $config->interval;
$newConfig->messages = $config->messages ?? json_encode([], JSON_UNESCAPED_UNICODE);
$newConfig->createTime = time();
$newConfig->updateTime = time();
$newConfig->save();
}
break;
}
Db::commit();

View File

@@ -67,6 +67,11 @@ class Workbench extends Model
return $this->hasOne('WorkbenchImportContact', 'workbenchId', 'id');
}
// 入群欢迎语配置关联
public function groupWelcome()
{
return $this->hasOne('WorkbenchGroupWelcome', 'workbenchId', 'id');
}
/**
* 用户关联

View File

@@ -0,0 +1,27 @@
<?php
namespace app\cunkebao\model;
use think\Model;
/**
* 入群欢迎语工作台模型
*/
class WorkbenchGroupWelcome extends Model
{
protected $table = 'ck_workbench_group_welcome';
protected $pk = 'id';
protected $name = 'workbench_group_welcome';
// 自动写入时间戳
protected $autoWriteTimestamp = true;
protected $createTime = 'createTime';
protected $updateTime = 'updateTime';
// 定义关联的工作台
public function workbench()
{
return $this->belongsTo('Workbench', 'workbenchId', 'id');
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace app\cunkebao\model;
use think\Model;
/**
* 入群欢迎语发送记录模型
*/
class WorkbenchGroupWelcomeItem extends Model
{
protected $table = 'ck_workbench_group_welcome_item';
protected $pk = 'id';
protected $name = 'workbench_group_welcome_item';
// 自动写入时间戳
protected $autoWriteTimestamp = true;
protected $createTime = 'createTime';
protected $updateTime = 'updateTime';
// 状态常量
const STATUS_PENDING = 0; // 待发送
const STATUS_SENDING = 1; // 发送中
const STATUS_SUCCESS = 2; // 发送成功
const STATUS_FAILED = 3; // 发送失败
/**
* 定义关联的工作台
*/
public function workbench()
{
return $this->belongsTo('Workbench', 'workbenchId', 'id');
}
/**
* 获取状态文本
* @param int $status 状态值
* @return string
*/
public static function getStatusText($status)
{
$statusMap = [
self::STATUS_PENDING => '待发送',
self::STATUS_SENDING => '发送中',
self::STATUS_SUCCESS => '发送成功',
self::STATUS_FAILED => '发送失败',
];
return $statusMap[$status] ?? '未知';
}
}

View File

@@ -13,13 +13,14 @@ class Workbench extends Validate
const TYPE_GROUP_CREATE = 4; // 自动建群
const TYPE_TRAFFIC_DISTRIBUTION = 5; // 流量分发
const TYPE_IMPORT_CONTACT = 6; // 流量分发
const TYPE_GROUP_WELCOME = 7; // 入群欢迎语
/**
* 验证规则
*/
protected $rule = [
'name' => 'require|max:100',
'type' => 'require|in:1,2,3,4,5,6',
'type' => 'require|in:1,2,3,4,5,6,7',
//'autoStart' => 'require|boolean',
// 自动点赞特有参数
'interval' => 'requireIf:type,1|number|min:1',
@@ -62,8 +63,14 @@ class Workbench extends Validate
'maxPerDay' => 'requireIf:type,5|number|min:1',
'timeType' => 'requireIf:type,5|in:1,2',
'accountGroups' => 'requireIf:type,5|array|min:1',
// 入群欢迎语特有参数
'wechatGroups' => 'requireIf:type,7|array|min:1', // 入群欢迎语必须选择群组
'interval' => 'requireIf:type,7|number|min:1', // 间隔时间
'startTime' => 'requireIf:type,7|dateFormat:H:i', // 开始时间
'endTime' => 'requireIf:type,7|dateFormat:H:i', // 结束时间
'messages' => 'requireIf:type,7|array|min:1', // 欢迎消息列表
// 通用参数
'deviceGroups' => 'requireIf:type,1,2,5|array',
'deviceGroups' => 'requireIf:type,1,2,5,7|array',
'trafficPools' => 'checkFriendPushPools',
];
@@ -185,6 +192,7 @@ class Workbench extends Validate
'announcementContent', 'enableAiRewrite', 'aiRewritePrompt',
'groupNameTemplate', 'maxGroupsPerDay', 'groupSizeMin', 'groupSizeMax',
'distributeType', 'timeType', 'accountGroups',
'messages',
],
'update_status' => ['id', 'status'],
'update' => ['name', 'type', 'autoStart', 'deviceGroups', 'targetGroups',
@@ -194,6 +202,7 @@ class Workbench extends Validate
'announcementContent', 'enableAiRewrite', 'aiRewritePrompt',
'groupNameTemplate', 'maxGroupsPerDay', 'groupSizeMin', 'groupSizeMax',
'distributeType', 'timeType', 'accountGroups',
'messages',
]
];

View File

@@ -0,0 +1,441 @@
<?php
namespace app\job;
use app\api\controller\WebSocketController;
use app\cunkebao\model\WorkbenchGroupWelcomeItem;
use think\Db;
use think\facade\Log;
use think\facade\Env;
use think\queue\Job;
/**
* 入群欢迎语任务
*/
class WorkbenchGroupWelcomeJob
{
// 常量定义
const MAX_RETRY_ATTEMPTS = 3; // 最大重试次数
const RETRY_DELAY = 10; // 重试延迟(秒)
const MAX_JOIN_AGE_SECONDS = 86400; // 最大入群时间1天
const MSG_TYPE_TEXT = 1; // 普通文本消息
const MSG_TYPE_AT = 90001; // @人消息
const WORKBENCH_TYPE_WELCOME = 7; // 入群欢迎语类型
const STATUS_SUCCESS = 2; // 发送成功状态
/**
* 队列执行方法
* @param Job $job 队列任务
* @param array $data 任务数据
* @return void
*/
public function fire(Job $job, $data)
{
try {
if ($this->processWelcomeMessage($data, $job->attempts())) {
$job->delete();
} else {
if ($job->attempts() > self::MAX_RETRY_ATTEMPTS) {
Log::error('入群欢迎语任务执行失败,已超过重试次数,数据:' . json_encode($data));
$job->delete();
} else {
Log::warning('入群欢迎语任务执行失败,重试次数:' . $job->attempts() . ',数据:' . json_encode($data));
$job->release(self::RETRY_DELAY);
}
}
} catch (\Exception $e) {
Log::error('入群欢迎语任务异常:' . $e->getMessage());
if ($job->attempts() > self::MAX_RETRY_ATTEMPTS) {
$job->delete();
} else {
$job->release(self::RETRY_DELAY);
}
}
}
/**
* 处理欢迎消息发送
* @param array $data 任务数据
* @param int $attempts 重试次数
* @return bool
*/
public function processWelcomeMessage($data, $attempts)
{
try {
// 查找该群配置的入群欢迎语工作台
$welcomeConfigs = Db::table('ck_workbench_group_welcome')
->alias('wgw')
->join('ck_workbench w', 'w.id = wgw.workbenchId')
->where('w.status', 1) // 工作台启用
->where('w.type', self::WORKBENCH_TYPE_WELCOME) // 入群欢迎语类型
->field('wgw.*,w.id as workbenchId')
->select();
if (empty($welcomeConfigs)) {
return true; // 没有配置欢迎语,不算失败
}
foreach ($welcomeConfigs as $config) {
// 解析配置中的群组列表
$wechatGroups = json_decode($config['groups'] ?? '[]', true);
if (!is_array($wechatGroups) || empty($wechatGroups)) {
continue; // 该配置没有配置群组,跳过
}
// 遍历该配置中的每个群ID处理每个群的欢迎语
foreach ($wechatGroups as $groupItemId) {
// 检查群是否存在
$chatroomExists = Db::table('s2_wechat_chatroom')
->where('id', $groupItemId)
->where('isDeleted', 0)
->count();
if (!$chatroomExists) {
Log::warning("群ID {$groupItemId} 不存在或已删除,跳过欢迎语处理");
continue;
}
// 处理单个群的欢迎语
$this->processSingleGroupWelcome($groupItemId, $config);
}
}
return true;
} catch (\Exception $e) {
Log::error('处理入群欢迎语异常:' . $e->getMessage() . ', 数据:' . json_encode($data));
return false;
}
}
/**
* 处理单个群的欢迎语发送
* @param int $groupId 群IDs2_wechat_chatroom表的id
* @param array $config 工作台配置
* @return void
*/
protected function processSingleGroupWelcome($groupId, $config)
{
// 根据groupId获取群信息
$chatroom = Db::table('s2_wechat_chatroom')
->where('id', $groupId)
->where('isDeleted', 0)
->field('wechatAccountId,wechatAccountWechatId')
->find();
if (empty($chatroom)) {
Log::warning("群ID {$groupId} 不存在或已删除,跳过欢迎语处理");
return;
}
// 检查时间范围
if (!$this->isInTimeRange($config['startTime'] ?? '', $config['endTime'] ?? '')) {
return; // 不在工作时间范围内
}
// 解析消息列表
$messages = json_decode($config['messages'] ?? '[]', true);
if (empty($messages) || !is_array($messages)) {
return; // 没有配置消息
}
// interval代表整组消息的时间间隔在此间隔内进群的成员都需要@
$interval = intval($config['interval'] ?? 0); // 秒
// 查找该群最近一次发送欢迎语的时间
$lastWelcomeTime = Db::table('ck_workbench_group_welcome_item')
->where('workbenchId', $config['workbenchId'])
->where('groupid', $groupId)
->where('status', self::STATUS_SUCCESS) // 发送成功
->order('sendTime', 'desc')
->value('sendTime');
// 确定时间窗口起点
if (!empty($lastWelcomeTime)) {
// 如果上次发送时间在interval内说明还在同一个时间窗口需要累积新成员
$windowStartTime = max($lastWelcomeTime, time() - $interval);
} else {
// 第一次发送从interval前开始
$windowStartTime = time() - $interval;
}
// 查询该群在时间窗口内的新成员
// 通过关联s2_wechat_chatroom表查询使用groupId
$recentMembers = Db::table('s2_wechat_chatroom_member')
->alias('wcm')
->join(['s2_wechat_chatroom' => 'wc'], 'wc.chatroomId = wcm.chatroomId')
->where('wc.id', $groupId)
->where('wcm.createTime', '>=', $windowStartTime)
->field('wcm.wechatId,wcm.nickname,wcm.createTime')
->select();
// 入群太久远的成员不要 @,只保留「近期加入」的成员
$minJoinTime = time() - self::MAX_JOIN_AGE_SECONDS;
$recentMembers = array_values(array_filter($recentMembers, function ($member) use ($minJoinTime) {
$joinTime = intval($member['createTime'] ?? 0);
return $joinTime >= $minJoinTime;
}));
if (empty($recentMembers)) {
return;
}
// 如果上次发送时间在interval内检查是否有新成员
if (!empty($lastWelcomeTime) && $lastWelcomeTime >= (time() - $interval)) {
// 获取上次发送时的成员列表
$lastWelcomeItem = Db::table('ck_workbench_group_welcome_item')
->where('workbenchId', $config['workbenchId'])
->where('groupid', $groupId)
->where('sendTime', $lastWelcomeTime)
->field('friendId')
->find();
$lastMemberIds = json_decode($lastWelcomeItem['friendId'] ?? '[]', true);
$currentMemberWechatIds = array_column($recentMembers, 'wechatId');
// 找出新加入的成员
$newMemberWechatIds = array_diff($currentMemberWechatIds, $lastMemberIds);
if (empty($newMemberWechatIds)) {
return; // 没有新成员,跳过
}
// 只发送给新加入的成员
$membersToWelcome = [];
foreach ($recentMembers as $member) {
if (in_array($member['wechatId'], $newMemberWechatIds)) {
$membersToWelcome[] = $member;
}
}
} else {
// 不在同一个时间窗口,@所有在时间间隔内的成员
$membersToWelcome = $recentMembers;
}
if (empty($membersToWelcome)) {
return;
}
// 获取设备信息(用于发送消息)
$devices = json_decode($config['devices'] ?? '[]', true);
if (empty($devices) || !is_array($devices)) {
return;
}
// wechatAccountId 是 s2_wechat_account 表的 id
$wechatAccountId = $chatroom['wechatAccountId'] ?? 0;
$wechatAccountWechatId = $chatroom['wechatAccountWechatId'] ?? '';
if (empty($wechatAccountWechatId)) {
Log::warning("群ID {$groupId} 的微信账号ID为空跳过欢迎语发送");
return;
}
// 初始化WebSocket
$username = Env::get('api.username', '');
$password = Env::get('api.password', '');
$toAccountId = '';
if (!empty($username) || !empty($password)) {
$toAccountId = Db::name('users')->where('account', $username)->value('s2_accountId');
}
$webSocket = new WebSocketController(['userName' => $username, 'password' => $password, 'accountId' => $toAccountId]);
// 按order排序消息
usort($messages, function($a, $b) {
return (intval($a['order'] ?? 0)) <=> (intval($b['order'] ?? 0));
});
// 发送每条消息
foreach ($messages as $messageIndex => $message) {
$messageContent = $message['content'] ?? '';
$sendInterval = intval($message['sendInterval'] ?? 5); // 秒
$intervalUnit = $message['intervalUnit'] ?? 'seconds';
// 转换间隔单位
$sendInterval = $this->convertIntervalToSeconds($sendInterval, $intervalUnit);
// 替换 @{好友} 占位符
$processedContent = $this->replaceFriendPlaceholder($messageContent, $membersToWelcome);
// 构建@消息格式
$atContent = $this->buildAtMessage($processedContent, $membersToWelcome);
// 判断是否有@人如果有atId则使用90001否则使用1
$hasAtMembers = !empty($atContent['atId']);
$msgType = $hasAtMembers ? self::MSG_TYPE_AT : self::MSG_TYPE_TEXT;
// 发送消息
// 注意wechatChatroomId 使用 groupId数字类型不是 chatroomId
$sendResult = $webSocket->sendCommunitys([
'content' => json_encode($atContent, JSON_UNESCAPED_UNICODE),
'msgType' => $msgType,
'wechatAccountId' => intval($wechatAccountId),
'wechatChatroomId' => $groupId, // 使用 groupId数字类型
]);
$sendResultData = json_decode($sendResult, true);
$sendSuccess = !empty($sendResultData) && isset($sendResultData['code']) && $sendResultData['code'] == 200;
// 记录发送记录
$friendIds = array_column($membersToWelcome, 'wechatId');
$this->saveWelcomeItem([
'workbenchId' => $config['workbenchId'],
'groupId' => $groupId,
'deviceId' => !empty($devices) ? intval($devices[0]) : 0,
'wechatAccountId' => $wechatAccountId,
'friendId' => $friendIds,
'status' => $sendSuccess ? WorkbenchGroupWelcomeItem::STATUS_SUCCESS : WorkbenchGroupWelcomeItem::STATUS_FAILED,
'messageIndex' => $messageIndex,
'messageId' => $message['id'] ?? '',
'content' => $processedContent,
'sendTime' => time(),
'errorMsg' => $sendSuccess ? '' : ($sendResultData['msg'] ?? '发送失败'),
]);
// 如果不是最后一条消息,等待间隔时间
if ($messageIndex < count($messages) - 1) {
sleep($sendInterval);
}
}
Log::info("入群欢迎语发送成功工作台ID: {$config['workbenchId']}, 群ID: {$groupId}, 成员数: " . count($membersToWelcome));
}
/**
* 替换 @{好友} 占位符为群成员昵称(带@符号)
* @param string $content 原始内容
* @param array $members 成员列表
* @return string 替换后的内容
*/
protected function replaceFriendPlaceholder($content, $members)
{
if (empty($members)) {
return str_replace('@{好友}', '', $content);
}
// 将所有成员的昵称拼接,每个昵称前添加@符号
$atNicknames = [];
foreach ($members as $member) {
$nickname = $member['nickname'] ?? '';
if (!empty($nickname)) {
$atNicknames[] = '@' . $nickname;
} else {
// 如果没有昵称使用wechatId
$wechatId = $member['wechatId'] ?? '';
if (!empty($wechatId)) {
$atNicknames[] = '@' . $wechatId;
}
}
}
$atNicknameStr = implode(' ', $atNicknames);
// 替换 @{好友} 为 @昵称1 @昵称2 ...
$content = str_replace('@{好友}', $atNicknameStr, $content);
return $content;
}
/**
* 构建@消息格式
* @param string $text 文本内容(已替换@{好友}占位符,已包含@符号)
* @param array $members 成员列表
* @return array 格式:{"text":"@wong @wong 11111111","atId":"WANGMINGZHENG000,WANGMINGZHENG000"}
*/
protected function buildAtMessage($text, $members)
{
$atIds = [];
// 收集所有成员的wechatId用于atId
foreach ($members as $member) {
$wechatId = $member['wechatId'] ?? '';
if (!empty($wechatId)) {
$atIds[] = $wechatId;
}
}
// 文本中已经包含了@昵称在replaceFriendPlaceholder中已添加
// 直接使用处理后的文本
return [
'text' => trim($text),
'atId' => implode(',', $atIds)
];
}
/**
* 检查是否在工作时间范围内
* @param string $startTime 开始时间格式HH:mm
* @param string $endTime 结束时间格式HH:mm
* @return bool
*/
protected function isInTimeRange($startTime, $endTime)
{
if (empty($startTime) || empty($endTime)) {
return true; // 如果没有配置时间,默认全天可用
}
$currentTime = date('H:i');
$currentMinutes = $this->timeToMinutes($currentTime);
$startMinutes = $this->timeToMinutes($startTime);
$endMinutes = $this->timeToMinutes($endTime);
if ($startMinutes <= $endMinutes) {
// 正常情况09:00 - 21:00
return $currentMinutes >= $startMinutes && $currentMinutes <= $endMinutes;
} else {
// 跨天情况21:00 - 09:00
return $currentMinutes >= $startMinutes || $currentMinutes <= $endMinutes;
}
}
/**
* 将时间转换为分钟数
* @param string $time 时间格式HH:mm
* @return int 分钟数
*/
protected function timeToMinutes($time)
{
$parts = explode(':', $time);
if (count($parts) != 2) {
return 0;
}
return intval($parts[0]) * 60 + intval($parts[1]);
}
/**
* 转换间隔单位到秒
* @param int $interval 间隔数值
* @param string $unit 单位seconds/minutes/hours
* @return int 秒数
*/
protected function convertIntervalToSeconds($interval, $unit)
{
switch ($unit) {
case 'minutes':
return $interval * 60;
case 'hours':
return $interval * 3600;
case 'seconds':
default:
return $interval;
}
}
/**
* 保存欢迎语发送记录
* @param array $data 记录数据
* @return void
*/
protected function saveWelcomeItem($data)
{
try {
$item = new WorkbenchGroupWelcomeItem();
$item->workbenchId = $data['workbenchId'];
$item->groupId = $data['groupId'];
$item->deviceId = $data['deviceId'] ?? 0;
$item->wechatAccountId = $data['wechatAccountId'] ?? 0;
$item->friendId = json_encode($data['friendId'] ?? [], JSON_UNESCAPED_UNICODE);
$item->status = $data['status'] ?? WorkbenchGroupWelcomeItem::STATUS_PENDING;
$item->messageIndex = $data['messageIndex'] ?? null;
$item->messageId = $data['messageId'] ?? '';
$item->content = $data['content'] ?? '';
$item->sendTime = $data['sendTime'] ?? time();
$item->errorMsg = $data['errorMsg'] ?? '';
$item->retryCount = 0;
$item->createTime = time();
$item->updateTime = time();
$item->save();
} catch (\Exception $e) {
Log::error('保存入群欢迎语记录失败:' . $e->getMessage());
}
}
}