Skip to content

消息系统

MineShop 内置完整的消息系统,支持多渠道推送、消息模板、用户偏好设置等功能。

🎯 系统架构

┌─────────────────────────────────────────────────────────────┐
│                      消息服务 (MessageService)                │
├─────────────────────────────────────────────────────────────┤
│  创建消息  │  发送消息  │  定时发送  │  批量发送  │  统计分析  │
├─────────────────────────────────────────────────────────────┤
│                    通知服务 (NotificationService)             │
├─────────────────────────────────────────────────────────────┤
│  站内信  │  邮件  │  短信  │  推送  │  小程序  │  WebSocket  │
└─────────────────────────────────────────────────────────────┘

✨ 核心特性

1. 多渠道支持

渠道说明状态
database站内信✅ 已实现
email邮件通知🔧 需配置
sms短信通知🔧 需配置
pushAPP 推送🔧 需配置
miniapp小程序订阅消息🔧 需配置
websocket实时推送🔧 需配置

2. 消息类型

php
enum MessageType: string
{
    case SYSTEM = 'system';           // 系统通知
    case ANNOUNCEMENT = 'announcement'; // 公告
    case ALERT = 'alert';             // 警告
    case REMINDER = 'reminder';       // 提醒
    case MARKETING = 'marketing';     // 营销
}

3. 消息服务

php
// MessageService.php
class MessageService
{
    // 创建消息
    public function create(array $data): Message
    {
        $this->validateMessageData($data);
        $data = $this->setDefaultValues($data);
        return $this->repository->create($data);
    }

    // 发送消息
    public function send(int $messageId): bool
    {
        $message = $this->repository->findById($messageId);
        $recipients = $message->getRecipients();
        
        Db::transaction(function () use ($message, $recipients) {
            $message->markAsSending();
            $this->createUserMessages($message, $recipients);
            $message->markAsSent();
        });
        
        // 异步推送到各渠道
        $this->queueNotifications($message, $recipients);
        
        return true;
    }

    // 定时发送
    public function schedule(int $messageId, Carbon $scheduledAt): bool
    {
        return $message->update([
            'scheduled_at' => $scheduledAt,
            'status' => MessageStatus::SCHEDULED->value
        ]);
    }
}

4. 用户偏好设置

php
// NotificationService.php
public function getDefaultPreferences(): array
{
    return [
        'channel_preferences' => [
            'database' => true,
            'email' => false,
            'sms' => false,
            'push' => false
        ],
        'type_preferences' => [
            'system' => true,
            'announcement' => true,
            'alert' => true,
            'reminder' => true,
            'marketing' => false
        ],
        'do_not_disturb_enabled' => false,
        'do_not_disturb_start' => '22:00:00',
        'do_not_disturb_end' => '08:00:00',
        'min_priority' => 1,
    ];
}

// 检查是否应该发送
protected function shouldSendNotification(Message $message, int $userId, string $channel): bool
{
    $preference = $this->preferenceRepository->getUserPreference($userId);
    
    // 检查渠道偏好
    if (!$preference->getChannelSetting($channel)) {
        return false;
    }
    
    // 检查消息类型偏好
    if (!$preference->getTypeSetting($message->type)) {
        return false;
    }
    
    // 检查优先级
    if ($message->priority < $preference->min_priority) {
        return false;
    }
    
    return true;
}

5. 免打扰时段

php
protected function isInDoNotDisturbTime(int $userId): bool
{
    $preference = $this->preferenceRepository->getUserPreference($userId);
    
    if (!$preference->do_not_disturb_enabled) {
        return false;
    }
    
    $now = Carbon::now();
    $startTime = Carbon::createFromTimeString($preference->do_not_disturb_start);
    $endTime = Carbon::createFromTimeString($preference->do_not_disturb_end);
    
    // 处理跨天情况
    if ($startTime->greaterThan($endTime)) {
        return $now->gte($startTime) || $now->lte($endTime);
    }
    
    return $now->between($startTime, $endTime);
}

🔄 消息状态

┌──────────┐    发送    ┌──────────┐    完成    ┌──────────┐
│  draft   │ ────────→ │ sending  │ ────────→ │   sent   │
│   草稿   │           │  发送中   │           │  已发送   │
└──────────┘           └──────────┘           └──────────┘
      │                      │
      │ 定时                  │ 失败
      ↓                      ↓
┌──────────┐           ┌──────────┐
│ scheduled│           │  failed  │
│  已定时   │           │  失败    │
└──────────┘           └──────────┘

📦 数据结构

消息表 (messages)

字段类型说明
titlestring消息标题
contenttext消息内容
typestring消息类型
priorityint优先级 (1-5)
recipient_typestring接收者类型
channelsjson推送渠道
statusstring状态
scheduled_atdatetime定时发送时间

用户消息表 (user_messages)

字段类型说明
user_idint用户 ID
message_idint消息 ID
is_readbool是否已读
read_atdatetime阅读时间
is_deletedbool是否删除

💻 API 接口

后台管理

接口方法说明
/admin/messageGET消息列表
/admin/messagePOST创建消息
/admin/message/{id}PUT更新消息
/admin/message/{id}/sendPOST发送消息
/admin/message/statisticsGET统计数据

用户端

接口方法说明
/api/message/listGET我的消息
/api/message/{id}GET消息详情
/api/message/{id}/readPOST标记已读
/api/message/read-allPOST全部已读
/api/message/unread-countGET未读数量
/api/message/preferencesGET偏好设置
/api/message/preferencesPUT更新偏好

🔧 配置说明

php
// config/autoload/system_message.php
return [
    'message' => [
        'max_title_length' => 255,
        'max_content_length' => 10000,
        'default_priority' => 1,
        'retention_days' => 90,  // 消息保留天数
    ],
    'notification' => [
        'default_channels' => [
            'database' => true,
            'email' => false,
            'sms' => false,
            'push' => false,
        ],
    ],
    'queue' => [
        'channel' => 'default',
    ],
];

📊 统计功能

php
public function getStatistics(): array
{
    return [
        'total' => Message::count(),
        'sent' => Message::where('status', 'sent')->count(),
        'pending' => Message::where('status', 'draft')->count(),
        'by_type' => Message::groupBy('type')
            ->selectRaw('type, count(*) as count')
            ->get(),
    ];
}

⚠️ 注意事项

  1. 异步发送: 大量消息使用队列异步发送
  2. 用户偏好: 尊重用户的通知偏好设置
  3. 免打扰: 非紧急消息遵守免打扰时段
  4. 消息清理: 定期清理过期消息

📚 相关文档

基于 Apache-2.0 许可发布 | 感谢 MineAdmin 提供的优秀基础框架