feat(jpush): add vendor-specific message classification support for push notifications
All checks were successful
Build Backend / build (push) Successful in 3m4s
Build Backend / build-docker (push) Successful in 1m15s

- 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:
lafay
2026-06-18 19:41:32 +08:00
parent 5b83f98fb8
commit 821e066446
13 changed files with 637 additions and 66 deletions

View File

@@ -39,12 +39,32 @@ func (huaweiConverter) Convert(params MessageParams, cfg config.ChannelConfig) m
return channelOnlyMap(params.Scene, cfg.Vendor.Huawei.System, cfg.Vendor.Huawei.Chat, cfg.System, cfg.Chat)
}
// vivoConverter VIVO 厂商转换器(仅 channel_id
// vivoConverter VIVO 厂商转换器
// channel_id + category厂商消息场景标识
// classification=1 时 category 必须为系统消息类,不携带 category 会默认按运营消息下发受频控限制
type vivoConverter struct{}
func (vivoConverter) VendorName() string { return "vivo" }
func (vivoConverter) Convert(params MessageParams, cfg config.ChannelConfig) map[string]any {
return channelOnlyMap(params.Scene, cfg.Vendor.VIVO.System, cfg.Vendor.VIVO.Chat, cfg.System, cfg.Chat)
v := cfg.Vendor.VIVO
cid := resolveChannelID(params.Scene, v.System, v.Chat, cfg.System, cfg.Chat)
category := v.VivoChatCategory
if params.Scene == SceneSystem {
category = v.VivoSystemCategory
}
if cid == "" && category == "" {
return nil
}
m := map[string]any{}
if cid != "" {
m["channel_id"] = cid
}
if category != "" {
m["category"] = category
}
return m
}
// meizuConverter 魅族厂商转换器(仅 channel_id
@@ -55,12 +75,33 @@ func (meizuConverter) Convert(params MessageParams, cfg config.ChannelConfig) ma
return channelOnlyMap(params.Scene, cfg.Vendor.Meizu.System, cfg.Vendor.Meizu.Chat, cfg.System, cfg.Chat)
}
// honorConverter 荣耀厂商转换器(仅 channel_id
// honorConverter 荣耀厂商转换器
// channel_id + importance通知栏消息智能分类
// importance 取值:"NORMAL"=服务与通讯,"LOW"=资讯营销
// 注classification 优先级更高,会覆盖 importance 设置的值
type honorConverter struct{}
func (honorConverter) VendorName() string { return "honor" }
func (honorConverter) Convert(params MessageParams, cfg config.ChannelConfig) map[string]any {
return channelOnlyMap(params.Scene, cfg.Vendor.Honor.System, cfg.Vendor.Honor.Chat, cfg.System, cfg.Chat)
h := cfg.Vendor.Honor
cid := resolveChannelID(params.Scene, h.System, h.Chat, cfg.System, cfg.Chat)
importance := h.HonorChatImportance
if params.Scene == SceneSystem {
importance = h.HonorSystemImportance
}
if cid == "" && importance == "" {
return nil
}
m := map[string]any{}
if cid != "" {
m["channel_id"] = cid
}
if importance != "" {
m["importance"] = importance
}
return m
}
// fcmConverter FCM 厂商转换器(仅 channel_id
@@ -104,12 +145,22 @@ func (xiaomiConverter) Convert(params MessageParams, cfg config.ChannelConfig) m
}
// buildMiTemplateParam 生成小米消息模板参数 JSON 字符串
// 默认占位符sender / title / content可按实际小米模板占位符在集中处调整
// 占位符严格匹配小米模板定义的 keywords
// - 私聊模板M10289 好友聊天): keywords1=title, keywords2=发送者, keywords3=内容
// - 系统模板P10761 系统消息): keywords1=标题, keywords2=内容
func buildMiTemplateParam(params MessageParams) string {
param := map[string]any{
"sender": params.SenderName,
"title": params.Title,
"content": params.Body,
var param map[string]any
if params.Scene == SceneSystem {
param = map[string]any{
"keywords1": params.Title,
"keywords2": params.Body,
}
} else {
param = map[string]any{
"keywords1": params.Title,
"keywords2": params.SenderName,
"keywords3": params.Body,
}
}
b, err := json.Marshal(param)
if err != nil {
@@ -129,11 +180,15 @@ func (oppoConverter) Convert(params MessageParams, cfg config.ChannelConfig) map
cid := resolveChannelID(params.Scene, o.System, o.Chat, cfg.System, cfg.Chat)
// OPPO 私信模板按场景选择
templateID := o.OppoChatPrivateTemplateID
category := o.OppoChatCategory
notifyLevel := o.OppoChatNotifyLevel
if params.Scene == SceneSystem {
templateID = o.OppoSystemPrivateTemplateID
category = o.OppoSystemCategory
notifyLevel = o.OppoSystemNotifyLevel
}
if cid == "" && templateID == "" {
if cid == "" && templateID == "" && category == "" && notifyLevel == 0 {
return nil
}
@@ -152,5 +207,14 @@ func (oppoConverter) Convert(params MessageParams, cfg config.ChannelConfig) map
"sender": params.SenderName,
}
}
// OPPO 2024.11.20 新规category 消息场景标识notify_level 提醒等级
// 文档约束:使用 notify_level 时 category 必传,否则 OPPO 校验失败
// 这里做保护notify_level 非零但 category 为空时跳过 notify_level
if category != "" {
m["category"] = category
if notifyLevel != 0 {
m["notify_level"] = notifyLevel
}
}
return m
}