- 后台微信机器人增加群内独立公告的分发推送模块 - 聊天室系统引入3秒离线延迟(防抖)防重复播报 - 优化聊天界面消息拉取过滤自身的欢迎或离场广播 - 管理员登录时的烟花特效同步至用户当前的前端显示
254 lines
7.0 KiB
PHP
254 lines
7.0 KiB
PHP
<?php
|
||
|
||
/**
|
||
* 文件功能:微信消息发送核心业务逻辑服务
|
||
*
|
||
* @author ChatRoom Laravel
|
||
*
|
||
* @version 1.0.0
|
||
*/
|
||
|
||
namespace App\Services\WechatBot;
|
||
|
||
use App\Jobs\SendWechatBotMessage;
|
||
use App\Models\SysParam;
|
||
use App\Models\User;
|
||
use Illuminate\Support\Facades\Redis;
|
||
|
||
class WechatNotificationService
|
||
{
|
||
protected array $config = [];
|
||
|
||
protected bool $globalEnabled = false;
|
||
|
||
public function __construct()
|
||
{
|
||
$param = SysParam::where('alias', 'wechat_bot_config')->first();
|
||
if ($param && ! empty($param->body)) {
|
||
$this->config = json_decode($param->body, true) ?? [];
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检查当前时间是否在全局配置的允许推送免打扰时间窗口内
|
||
*/
|
||
protected function isWithinNotificationWindow(): bool
|
||
{
|
||
$startTime = $this->config['global_notify']['start_time'] ?? '';
|
||
$endTime = $this->config['global_notify']['end_time'] ?? '';
|
||
|
||
// 如果没有配置起止时间,默认全天放行
|
||
if (! $startTime || ! $endTime) {
|
||
return true;
|
||
}
|
||
|
||
$nowTime = date('H:i');
|
||
|
||
if ($startTime < $endTime) {
|
||
return $nowTime >= $startTime && $nowTime <= $endTime;
|
||
} else {
|
||
// 跨天逻辑,如 22:00 -> 08:00
|
||
return $nowTime >= $startTime || $nowTime <= $endTime;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 管理员上线群通知
|
||
*/
|
||
public function notifyAdminOnline(User $user): void
|
||
{
|
||
if (! $this->isWithinNotificationWindow()) {
|
||
return;
|
||
}
|
||
|
||
if (empty($this->config['group_notify']['toggle_admin_online'])) {
|
||
return;
|
||
}
|
||
|
||
$groupWxid = $this->config['group_notify']['target_wxid'] ?? '';
|
||
if (! $groupWxid) {
|
||
return;
|
||
}
|
||
|
||
// 判断是否真的是管理员(这里假设大于等于某等级,比如 15)
|
||
$adminLevel = (int) Sysparam::getValue('level_kick', '15');
|
||
if ($user->user_level < $adminLevel) {
|
||
return;
|
||
}
|
||
|
||
$message = "👑 【管理员上线】\n"
|
||
."管理员 [{$user->username}] 刚刚登录了系统。";
|
||
|
||
SendWechatBotMessage::dispatch($groupWxid, $message);
|
||
}
|
||
|
||
/**
|
||
* 百家乐开奖群通知
|
||
*/
|
||
public function notifyBaccaratResult(string $historyText): void
|
||
{
|
||
if (! $this->isWithinNotificationWindow()) {
|
||
return;
|
||
}
|
||
|
||
if (empty($this->config['group_notify']['toggle_baccarat_result'])) {
|
||
return;
|
||
}
|
||
|
||
$groupWxid = $this->config['group_notify']['target_wxid'] ?? '';
|
||
if (! $groupWxid) {
|
||
return;
|
||
}
|
||
SendWechatBotMessage::dispatch($groupWxid, $historyText);
|
||
}
|
||
|
||
/**
|
||
* 彩票开奖群通知
|
||
*/
|
||
public function notifyLotteryResult(string $historyText): void
|
||
{
|
||
if (! $this->isWithinNotificationWindow()) {
|
||
return;
|
||
}
|
||
|
||
if (empty($this->config['group_notify']['toggle_lottery_result'])) {
|
||
return;
|
||
}
|
||
|
||
$groupWxid = $this->config['group_notify']['target_wxid'] ?? '';
|
||
if (! $groupWxid) {
|
||
return;
|
||
}
|
||
|
||
SendWechatBotMessage::dispatch($groupWxid, $historyText);
|
||
}
|
||
|
||
/**
|
||
* 发送自定义群内公告
|
||
*
|
||
* @throws \Exception
|
||
*/
|
||
public function sendCustomGroupAnnouncement(string $message): void
|
||
{
|
||
$groupWxid = $this->config['group_notify']['target_wxid'] ?? '';
|
||
if (! $groupWxid) {
|
||
throw new \Exception('未配置目标微信群 Wxid,无法发送公告消息');
|
||
}
|
||
|
||
SendWechatBotMessage::dispatch($groupWxid, $message);
|
||
}
|
||
|
||
/**
|
||
* 好友上线私聊通知(带冷却)
|
||
*/
|
||
public function notifyFriendsOnline(User $user): void
|
||
{
|
||
if (! $this->isWithinNotificationWindow()) {
|
||
return;
|
||
}
|
||
|
||
if (empty($this->config['personal_notify']['toggle_friend_online'])) {
|
||
return;
|
||
}
|
||
|
||
$cacheKey = "wechat_notify_cd:friend_online:{$user->id}";
|
||
if (Redis::exists($cacheKey)) {
|
||
return;
|
||
}
|
||
|
||
// 假定有好友关系模型 Friends (视具体业务而定,目前先预留或者查询好友)
|
||
$friends = $this->getUserFriends($user->id);
|
||
|
||
foreach ($friends as $friend) {
|
||
if ($friend->wxid) {
|
||
$message = "👋 【好友上线】\n您的好友 [{$user->username}] 刚刚上线了!";
|
||
SendWechatBotMessage::dispatch($friend->wxid, $message);
|
||
}
|
||
}
|
||
|
||
// 冷却 30 分钟
|
||
Redis::setex($cacheKey, 1800, 1);
|
||
}
|
||
|
||
/**
|
||
* 夫妻上线私聊通知(带冷却)
|
||
*/
|
||
public function notifySpouseOnline(User $user): void
|
||
{
|
||
if (! $this->isWithinNotificationWindow()) {
|
||
return;
|
||
}
|
||
|
||
if (empty($this->config['personal_notify']['toggle_spouse_online'])) {
|
||
return;
|
||
}
|
||
|
||
$cacheKey = "wechat_notify_cd:spouse_online:{$user->id}";
|
||
if (Redis::exists($cacheKey)) {
|
||
return;
|
||
}
|
||
|
||
// 获取伴侣
|
||
$spouse = $this->getUserSpouse($user);
|
||
if ($spouse && $spouse->wxid) {
|
||
$message = "❤️ 【伴侣上线】\n您的伴侣 [{$user->username}] 刚刚上线了!";
|
||
SendWechatBotMessage::dispatch($spouse->wxid, $message);
|
||
// 冷却 30 分钟
|
||
Redis::setex($cacheKey, 1800, 1);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 等级变动私聊通知
|
||
*/
|
||
public function notifyLevelChange(User $user, int $oldLevel, int $newLevel): void
|
||
{
|
||
if (! $this->isWithinNotificationWindow()) {
|
||
return;
|
||
}
|
||
|
||
if (empty($this->config['personal_notify']['toggle_level_change'])) {
|
||
return;
|
||
}
|
||
|
||
if (! $user->wxid || $newLevel <= $oldLevel) {
|
||
return;
|
||
}
|
||
|
||
$message = "✨ 【等级提升】\n"
|
||
."恭喜您!您的聊天室等级已从 LV{$oldLevel} 提升至 LV{$newLevel}!";
|
||
|
||
SendWechatBotMessage::dispatch($user->wxid, $message);
|
||
}
|
||
|
||
// ---------------------------------------------------------
|
||
// Helper Methods (Mocking the real data retrieval methods)
|
||
// ---------------------------------------------------------
|
||
|
||
protected function getUserFriends(int $userId)
|
||
{
|
||
// 假定有好友表
|
||
if (\Illuminate\Support\Facades\Schema::hasTable('friends')) {
|
||
return \Illuminate\Support\Facades\DB::table('friends')
|
||
->join('users', 'users.id', '=', 'friends.friend_id')
|
||
->where('friends.user_id', $userId)
|
||
->whereNotNull('users.wxid')
|
||
->select('users.*')
|
||
->get();
|
||
}
|
||
|
||
return collect([]);
|
||
}
|
||
|
||
protected function getUserSpouse(User $user)
|
||
{
|
||
// 如果有配偶字段
|
||
$mateName = $user->peiou ?? null;
|
||
if ($mateName && $mateName !== '无' && $mateName !== '') {
|
||
return User::where('username', $mateName)->whereNotNull('wxid')->first();
|
||
}
|
||
|
||
return null;
|
||
}
|
||
}
|