2026-06-17 20:41:55 +08:00
|
|
|
|
// Package vendor 提供厂商无关的消息参数抽象与各厂商推送字段转换。
|
|
|
|
|
|
//
|
|
|
|
|
|
// 设计要点:
|
|
|
|
|
|
// - 调用方只需提供厂商无关的 MessageParams,不感知厂商差异。
|
|
|
|
|
|
// - 由于无法预知目标设备的具体厂商,JPush 要求所有厂商私有配置全数下发,
|
|
|
|
|
|
// 由 JPush 服务端按目标设备厂商路由。
|
|
|
|
|
|
// - 双层架构:TextRenderer(基础层,产出 Android/iOS 通用通知)+
|
|
|
|
|
|
// PrivateConverter(附加层,产出各厂商 third_party_channel 私有字段)。
|
|
|
|
|
|
package vendor
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"with_you/internal/config"
|
|
|
|
|
|
"with_you/internal/pkg/jpush"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// 场景常量
|
|
|
|
|
|
const (
|
|
|
|
|
|
// SceneSystem 系统消息/通知场景
|
|
|
|
|
|
SceneSystem = "system"
|
|
|
|
|
|
// SceneChat 私聊/群聊消息场景
|
|
|
|
|
|
SceneChat = "chat"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// MessageParams 厂商无关的统一消息参数(超集,用不上的字段厂商可忽略)
|
|
|
|
|
|
type MessageParams struct {
|
|
|
|
|
|
// Scene 决定使用哪类 channel/模板:SceneSystem 或 SceneChat
|
|
|
|
|
|
Scene string
|
|
|
|
|
|
// Title 通知标题(如发送者昵称、群名:发送者)
|
|
|
|
|
|
Title string
|
|
|
|
|
|
// Body 通知正文(消息内容预览)
|
|
|
|
|
|
Body string
|
|
|
|
|
|
// SenderName 发送者昵称
|
|
|
|
|
|
SenderName string
|
|
|
|
|
|
// SenderID 发送者 ID
|
|
|
|
|
|
SenderID string
|
|
|
|
|
|
// ConversationName 会话名称(群聊为群名)
|
|
|
|
|
|
ConversationName string
|
|
|
|
|
|
// ConversationID 会话 ID
|
|
|
|
|
|
ConversationID string
|
|
|
|
|
|
// MessageType 消息类型标识(chat_message / 系统通知类型),填充到 extras.notification_type
|
|
|
|
|
|
MessageType string
|
|
|
|
|
|
// ActionTime 操作时间(RFC3339)
|
|
|
|
|
|
ActionTime string
|
|
|
|
|
|
// LargeIcon 大图标 URL(群头像/发送者头像)
|
|
|
|
|
|
LargeIcon string
|
|
|
|
|
|
// Extra 兜底扩展字段,合并进 android/ios 的 extras
|
|
|
|
|
|
Extra map[string]any
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// PushPayload 工厂统一产物,client 据此组装最终推送 payload
|
|
|
|
|
|
type PushPayload struct {
|
|
|
|
|
|
// Notification 含 android + ios,由 TextRenderer 产出
|
|
|
|
|
|
Notification *jpush.Notification
|
|
|
|
|
|
// TPC 各厂商私有字段(options.third_party_channel),由 PrivateConverter 产出
|
|
|
|
|
|
TPC *jpush.ThirdPartyChannel
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TextRenderer 通用文本渲染器(基础层)
|
|
|
|
|
|
// 产出所有厂商共享的通用通知:Android 通用通知 + iOS 通用通知
|
|
|
|
|
|
type TextRenderer interface {
|
|
|
|
|
|
// RenderAndroid 渲染 Android 通用通知(所有 Android 厂商共享)
|
|
|
|
|
|
RenderAndroid(params MessageParams, cfg config.ChannelConfig) *jpush.AndroidNotif
|
|
|
|
|
|
// RenderIOS 渲染 iOS 通用通知(按场景设置 thread-id 分组)
|
|
|
|
|
|
RenderIOS(params MessageParams, cfg config.ChannelConfig) *jpush.IOSNotif
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// PrivateConverter 厂商私有转换器(附加层)
|
|
|
|
|
|
// 产出 third_party_channel.{vendor} 的私有字段(channel_id、模板等)
|
|
|
|
|
|
type PrivateConverter interface {
|
|
|
|
|
|
// VendorName 返回厂商标识,对应 third_party_channel 的 key(xiaomi/oppo/huawei/...)
|
|
|
|
|
|
VendorName() string
|
|
|
|
|
|
// Convert 根据消息参数 + 全局配置产出该厂商私有字段 map
|
|
|
|
|
|
// 厂商专属 channel_id 为空时回退到全局 cfg.System/Chat
|
|
|
|
|
|
// 返回 nil 表示该厂商无需下发(如最终 channel_id 与模板均未配置)
|
|
|
|
|
|
Convert(params MessageParams, cfg config.ChannelConfig) map[string]any
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Factory 持有通用渲染器与全部厂商私有转换器
|
|
|
|
|
|
type Factory struct {
|
|
|
|
|
|
renderer TextRenderer
|
|
|
|
|
|
privateConverters []PrivateConverter
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// NewFactory 创建工厂,注册默认渲染器与全部厂商转换器
|
|
|
|
|
|
func NewFactory() *Factory {
|
|
|
|
|
|
return &Factory{
|
|
|
|
|
|
renderer: newDefaultRenderer(),
|
|
|
|
|
|
privateConverters: []PrivateConverter{
|
|
|
|
|
|
xiaomiConverter{},
|
|
|
|
|
|
huaweiConverter{},
|
|
|
|
|
|
oppoConverter{},
|
|
|
|
|
|
vivoConverter{},
|
|
|
|
|
|
meizuConverter{},
|
|
|
|
|
|
honorConverter{},
|
|
|
|
|
|
fcmConverter{},
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// NewFactoryWith 自定义渲染器与转换器创建工厂(便于测试)
|
|
|
|
|
|
func NewFactoryWith(renderer TextRenderer, converters []PrivateConverter) *Factory {
|
|
|
|
|
|
return &Factory{renderer: renderer, privateConverters: converters}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Build 根据统一参数与配置,组装完整的 PushPayload
|
|
|
|
|
|
// 调用通用渲染器产出 Notification,调用各私有转换器产出 ThirdPartyChannel
|
|
|
|
|
|
func (f *Factory) Build(params MessageParams, cfg config.ChannelConfig) *PushPayload {
|
|
|
|
|
|
android := f.renderer.RenderAndroid(params, cfg)
|
|
|
|
|
|
ios := f.renderer.RenderIOS(params, cfg)
|
|
|
|
|
|
|
|
|
|
|
|
notification := &jpush.Notification{
|
|
|
|
|
|
Alert: params.Body,
|
|
|
|
|
|
Android: android,
|
|
|
|
|
|
IOS: ios,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
tpc := &jpush.ThirdPartyChannel{}
|
|
|
|
|
|
for _, c := range f.privateConverters {
|
|
|
|
|
|
m := c.Convert(params, cfg)
|
|
|
|
|
|
if m == nil {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
2026-06-18 00:02:00 +08:00
|
|
|
|
tpc.Set(c.VendorName(), m)
|
2026-06-17 20:41:55 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return &PushPayload{
|
|
|
|
|
|
Notification: notification,
|
2026-06-18 00:02:00 +08:00
|
|
|
|
TPC: tpc.Normalize(),
|
2026-06-17 20:41:55 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|