feat(jpush): add vendor-specific message classification support for push notifications
- Add OPPO category/notify_level support (2024.11.20 regulation: category required when notify_level present) - Add Honor importance field (NORMAL=service/LOW=marketing) for notification classification - Add vivo category field (IM/ACCOUNT) for message scenario identification - Set Xiaomi channel/template defaults in code (system=153609, chat=153608, templates P10761/M10289) - Add classification option to JPush push payloads (0=operation, 1=system) - Update xiaomi template keywords mapping to match actual template placeholders - Add keyword search support for GetFollowers and GetFollowing endpoints - Bind new config fields to environment variables for OPPO/Honor/vivo
This commit is contained in:
191
internal/pkg/jpush/vendor/payload_compat_test.go
vendored
191
internal/pkg/jpush/vendor/payload_compat_test.go
vendored
@@ -76,6 +76,26 @@ func TestPayloadEmptyConfigMatchesLegacy(t *testing.T) {
|
||||
}
|
||||
}
|
||||
t.Logf("notification JSON: %s", jsonStr)
|
||||
|
||||
// 5. classification 必须为系统消息(1):聊天即时消息统一归系统消息避免限速
|
||||
if payload.Classification != ClassificationSystem {
|
||||
t.Errorf("Classification = %d, want %d (system)", payload.Classification, ClassificationSystem)
|
||||
}
|
||||
}
|
||||
|
||||
// TestClassificationByScene 验证:system 与 chat 场景的 classification 均为 1(系统消息)
|
||||
// 聊天即时消息归为系统消息,避免厂商运营消息限速影响送达
|
||||
func TestClassificationByScene(t *testing.T) {
|
||||
factory := NewFactory()
|
||||
cfg := config.ChannelConfig{}
|
||||
|
||||
for _, scene := range []string{SceneSystem, SceneChat} {
|
||||
params := MessageParams{Scene: scene, Title: "T", Body: "B"}
|
||||
payload := factory.Build(params, cfg)
|
||||
if payload.Classification != ClassificationSystem {
|
||||
t.Errorf("scene=%s: Classification = %d, want %d", scene, payload.Classification, ClassificationSystem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestPayloadWithChannelConfig 验证:配置 channel_id 后正确下发,且不影响通用通知字段
|
||||
@@ -129,6 +149,177 @@ func TestPayloadXiaomiTemplateOptional(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestOppoCategoryNotifyLevel 验证:OPPO category/notify_level 按场景区分,且 notify_level 必须伴 category
|
||||
func TestOppoCategoryNotifyLevel(t *testing.T) {
|
||||
factory := NewFactory()
|
||||
|
||||
// 场景1:chat 用 IM + 16(强提醒)
|
||||
cfgChat := config.ChannelConfig{
|
||||
Vendor: config.VendorChannel{
|
||||
OPPO: config.VendorChannelID{
|
||||
Chat: "oppo_chat",
|
||||
OppoChatCategory: "IM",
|
||||
OppoChatNotifyLevel: 16,
|
||||
},
|
||||
},
|
||||
}
|
||||
payload := factory.Build(MessageParams{Scene: SceneChat, Title: "T", Body: "B"}, cfgChat)
|
||||
if payload.TPC == nil || payload.TPC.OPPO == nil {
|
||||
t.Fatal("OPPO TPC should exist for chat scene")
|
||||
}
|
||||
if payload.TPC.OPPO["category"] != "IM" {
|
||||
t.Errorf("OPPO chat category = %v, want IM", payload.TPC.OPPO["category"])
|
||||
}
|
||||
if payload.TPC.OPPO["notify_level"] != 16 {
|
||||
t.Errorf("OPPO chat notify_level = %v, want 16", payload.TPC.OPPO["notify_level"])
|
||||
}
|
||||
|
||||
// 场景2:system 用 ACCOUNT + 2(通知栏+锁屏)
|
||||
cfgSys := config.ChannelConfig{
|
||||
Vendor: config.VendorChannel{
|
||||
OPPO: config.VendorChannelID{
|
||||
System: "oppo_sys",
|
||||
OppoSystemCategory: "ACCOUNT",
|
||||
OppoSystemNotifyLevel: 2,
|
||||
},
|
||||
},
|
||||
}
|
||||
payload = factory.Build(MessageParams{Scene: SceneSystem, Title: "T", Body: "B"}, cfgSys)
|
||||
if payload.TPC.OPPO["category"] != "ACCOUNT" {
|
||||
t.Errorf("OPPO system category = %v, want ACCOUNT", payload.TPC.OPPO["category"])
|
||||
}
|
||||
if payload.TPC.OPPO["notify_level"] != 2 {
|
||||
t.Errorf("OPPO system notify_level = %v, want 2", payload.TPC.OPPO["notify_level"])
|
||||
}
|
||||
}
|
||||
|
||||
// TestOppoNotifyLevelRequiresCategory 验证:notify_level 配置但 category 未配置时,跳过 notify_level(避免 OPPO 校验失败)
|
||||
func TestOppoNotifyLevelRequiresCategory(t *testing.T) {
|
||||
factory := NewFactory()
|
||||
cfg := config.ChannelConfig{
|
||||
Vendor: config.VendorChannel{
|
||||
OPPO: config.VendorChannelID{
|
||||
Chat: "oppo_chat",
|
||||
OppoChatNotifyLevel: 16, // 只配 notify_level,不配 category
|
||||
},
|
||||
},
|
||||
}
|
||||
payload := factory.Build(MessageParams{Scene: SceneChat, Title: "T", Body: "B"}, cfg)
|
||||
if payload.TPC == nil || payload.TPC.OPPO == nil {
|
||||
t.Fatal("OPPO TPC should exist (has channel_id)")
|
||||
}
|
||||
if _, ok := payload.TPC.OPPO["notify_level"]; ok {
|
||||
t.Error("OPPO should NOT contain notify_level when category not configured")
|
||||
}
|
||||
if _, ok := payload.TPC.OPPO["category"]; ok {
|
||||
t.Error("OPPO should NOT contain category when not configured")
|
||||
}
|
||||
// channel_id 仍正常下发
|
||||
if payload.TPC.OPPO["channel_id"] != "oppo_chat" {
|
||||
t.Errorf("OPPO channel_id = %v, want oppo_chat", payload.TPC.OPPO["channel_id"])
|
||||
}
|
||||
}
|
||||
|
||||
// TestHonorImportanceByScene 验证:荣耀 importance 按场景区分(chat=NORMAL, system=LOW)
|
||||
func TestHonorImportanceByScene(t *testing.T) {
|
||||
factory := NewFactory()
|
||||
cfg := config.ChannelConfig{
|
||||
Vendor: config.VendorChannel{
|
||||
Honor: config.VendorChannelID{
|
||||
Chat: "honor_chat",
|
||||
System: "honor_sys",
|
||||
HonorChatImportance: "NORMAL",
|
||||
HonorSystemImportance: "LOW",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// chat 场景 -> NORMAL
|
||||
payload := factory.Build(MessageParams{Scene: SceneChat, Title: "T", Body: "B"}, cfg)
|
||||
if payload.TPC == nil || payload.TPC.Honor == nil {
|
||||
t.Fatal("Honor TPC should exist for chat scene")
|
||||
}
|
||||
if payload.TPC.Honor["importance"] != "NORMAL" {
|
||||
t.Errorf("Honor chat importance = %v, want NORMAL", payload.TPC.Honor["importance"])
|
||||
}
|
||||
|
||||
// system 场景 -> LOW
|
||||
payload = factory.Build(MessageParams{Scene: SceneSystem, Title: "T", Body: "B"}, cfg)
|
||||
if payload.TPC.Honor["importance"] != "LOW" {
|
||||
t.Errorf("Honor system importance = %v, want LOW", payload.TPC.Honor["importance"])
|
||||
}
|
||||
}
|
||||
|
||||
// TestHonorImportanceOptional 验证:未配置 importance 时不下发,仅 channel_id
|
||||
func TestHonorImportanceOptional(t *testing.T) {
|
||||
factory := NewFactory()
|
||||
cfg := config.ChannelConfig{
|
||||
Vendor: config.VendorChannel{
|
||||
Honor: config.VendorChannelID{Chat: "honor_chat"},
|
||||
},
|
||||
}
|
||||
payload := factory.Build(MessageParams{Scene: SceneChat, Title: "T", Body: "B"}, cfg)
|
||||
if payload.TPC == nil || payload.TPC.Honor == nil {
|
||||
t.Fatal("Honor TPC should exist (has channel_id)")
|
||||
}
|
||||
if _, ok := payload.TPC.Honor["importance"]; ok {
|
||||
t.Error("Honor should NOT contain importance when not configured")
|
||||
}
|
||||
if payload.TPC.Honor["channel_id"] != "honor_chat" {
|
||||
t.Errorf("Honor channel_id = %v, want honor_chat", payload.TPC.Honor["channel_id"])
|
||||
}
|
||||
}
|
||||
|
||||
// TestVivoCategoryByScene 验证:vivo category 按场景区分
|
||||
func TestVivoCategoryByScene(t *testing.T) {
|
||||
factory := NewFactory()
|
||||
cfg := config.ChannelConfig{
|
||||
Vendor: config.VendorChannel{
|
||||
VIVO: config.VendorChannelID{
|
||||
Chat: "vivo_chat",
|
||||
System: "vivo_sys",
|
||||
VivoChatCategory: "IM",
|
||||
VivoSystemCategory: "ACCOUNT",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// chat 场景 -> IM
|
||||
payload := factory.Build(MessageParams{Scene: SceneChat, Title: "T", Body: "B"}, cfg)
|
||||
if payload.TPC == nil || payload.TPC.VIVO == nil {
|
||||
t.Fatal("VIVO TPC should exist for chat scene")
|
||||
}
|
||||
if payload.TPC.VIVO["category"] != "IM" {
|
||||
t.Errorf("VIVO chat category = %v, want IM", payload.TPC.VIVO["category"])
|
||||
}
|
||||
|
||||
// system 场景 -> ACCOUNT
|
||||
payload = factory.Build(MessageParams{Scene: SceneSystem, Title: "T", Body: "B"}, cfg)
|
||||
if payload.TPC.VIVO["category"] != "ACCOUNT" {
|
||||
t.Errorf("VIVO system category = %v, want ACCOUNT", payload.TPC.VIVO["category"])
|
||||
}
|
||||
}
|
||||
|
||||
// TestVivoCategoryOptional 验证:未配置 category 时不下发,仅 channel_id
|
||||
func TestVivoCategoryOptional(t *testing.T) {
|
||||
factory := NewFactory()
|
||||
cfg := config.ChannelConfig{
|
||||
Vendor: config.VendorChannel{
|
||||
VIVO: config.VendorChannelID{Chat: "vivo_chat"},
|
||||
},
|
||||
}
|
||||
payload := factory.Build(MessageParams{Scene: SceneChat, Title: "T", Body: "B"}, cfg)
|
||||
if payload.TPC == nil || payload.TPC.VIVO == nil {
|
||||
t.Fatal("VIVO TPC should exist (has channel_id)")
|
||||
}
|
||||
if _, ok := payload.TPC.VIVO["category"]; ok {
|
||||
t.Error("VIVO should NOT contain category when not configured")
|
||||
}
|
||||
if payload.TPC.VIVO["channel_id"] != "vivo_chat" {
|
||||
t.Errorf("VIVO channel_id = %v, want vivo_chat", payload.TPC.VIVO["channel_id"])
|
||||
}
|
||||
}
|
||||
|
||||
func mustJSON(v any) string {
|
||||
b, _ := json.Marshal(v)
|
||||
return string(b)
|
||||
|
||||
Reference in New Issue
Block a user