Files
backend/internal/pkg/jpush/vendor/converters.go
lafay a0e210feab
All checks were successful
Build Backend / build (push) Successful in 3m1s
Build Backend / build-docker (push) Successful in 2m10s
feat(server): implement chat file TTL cleanup and enhance JPush vendor channel support
Add FileCleanupWorker for automatic expiration of chat files with configurable retention period and batch processing. Files uploaded via `/api/v1/uploads/files` are tracked in `uploaded_files` table with expiration timestamps, then deleted from S3 and database upon expiry. Expired file URLs are injected as `"expired": true` in message responses.

Extend JPush push notification configuration with vendor-specific channel_id support (xiaomi, huawei, oppo, vivo, meizu, honor, fcm) for differentiating system vs chat notifications. Add iOS APNs thread-id grouping configuration for notification categorization.
2026-06-17 20:41:55 +08:00

157 lines
5.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package vendor
import (
"encoding/json"
"with_you/internal/config"
)
// resolveChannelID 按场景选择 channel_id厂商专属值优先空则回退到全局默认
// scene 为 SceneSystem 时取 system 配置,其余取 chat 配置
func resolveChannelID(scene, vendorSys, vendorChat, defaultSys, defaultChat string) string {
if scene == SceneSystem {
if vendorSys != "" {
return vendorSys
}
return defaultSys
}
if vendorChat != "" {
return vendorChat
}
return defaultChat
}
// channelOnlyMap 仅 channel_id 厂商的通用转换逻辑(厂商空则回退全局默认)
// 最终 channel_id 为空时返回 nil不下发该厂商
func channelOnlyMap(scene, vendorSys, vendorChat, defaultSys, defaultChat string) map[string]any {
cid := resolveChannelID(scene, vendorSys, vendorChat, defaultSys, defaultChat)
if cid == "" {
return nil
}
return map[string]any{"channel_id": cid}
}
// huaweiConverter 华为厂商转换器(仅 channel_id
type huaweiConverter struct{}
func (huaweiConverter) VendorName() string { return "huawei" }
func (huaweiConverter) Convert(params MessageParams, cfg config.ChannelConfig) map[string]any {
return channelOnlyMap(params.Scene, cfg.Vendor.Huawei.System, cfg.Vendor.Huawei.Chat, cfg.System, cfg.Chat)
}
// vivoConverter VIVO 厂商转换器(仅 channel_id
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)
}
// meizuConverter 魅族厂商转换器(仅 channel_id
type meizuConverter struct{}
func (meizuConverter) VendorName() string { return "meizu" }
func (meizuConverter) Convert(params MessageParams, cfg config.ChannelConfig) map[string]any {
return channelOnlyMap(params.Scene, cfg.Vendor.Meizu.System, cfg.Vendor.Meizu.Chat, cfg.System, cfg.Chat)
}
// honorConverter 荣耀厂商转换器(仅 channel_id
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)
}
// fcmConverter FCM 厂商转换器(仅 channel_id
type fcmConverter struct{}
func (fcmConverter) VendorName() string { return "fcm" }
func (fcmConverter) Convert(params MessageParams, cfg config.ChannelConfig) map[string]any {
return channelOnlyMap(params.Scene, cfg.Vendor.FCM.System, cfg.Vendor.FCM.Chat, cfg.System, cfg.Chat)
}
// xiaomiConverter 小米厂商转换器
// 配置 mi_template_id 时,私信消息下发时携带 channel_id 及 mi_template_id + mi_template_param
type xiaomiConverter struct{}
func (xiaomiConverter) VendorName() string { return "xiaomi" }
func (xiaomiConverter) Convert(params MessageParams, cfg config.ChannelConfig) map[string]any {
x := cfg.Vendor.Xiaomi
cid := resolveChannelID(params.Scene, x.System, x.Chat, cfg.System, cfg.Chat)
// 小米模板按场景选择
templateID := x.MiChatTemplateID
if params.Scene == SceneSystem {
templateID = x.MiSystemTemplateID
}
// 最终 channel_id 与模板均未配置则跳过
if cid == "" && templateID == "" {
return nil
}
m := map[string]any{}
if cid != "" {
m["channel_id"] = cid
}
if templateID != "" {
m["mi_template_id"] = templateID
// mi_template_param 为模板参数的 JSON 字符串
m["mi_template_param"] = buildMiTemplateParam(params)
}
return m
}
// buildMiTemplateParam 生成小米消息模板参数 JSON 字符串
// 默认占位符sender / title / content可按实际小米模板占位符在集中处调整
func buildMiTemplateParam(params MessageParams) string {
param := map[string]any{
"sender": params.SenderName,
"title": params.Title,
"content": params.Body,
}
b, err := json.Marshal(param)
if err != nil {
return "{}"
}
return string(b)
}
// oppoConverter OPPO 厂商转换器
// 配置 private_msg_template_id 时,私信消息下发时携带 private_msg_template_id + private_title_parameters + private_content_parameters
type oppoConverter struct{}
func (oppoConverter) VendorName() string { return "oppo" }
func (oppoConverter) Convert(params MessageParams, cfg config.ChannelConfig) map[string]any {
o := cfg.Vendor.OPPO
cid := resolveChannelID(params.Scene, o.System, o.Chat, cfg.System, cfg.Chat)
// OPPO 私信模板按场景选择
templateID := o.OppoChatPrivateTemplateID
if params.Scene == SceneSystem {
templateID = o.OppoSystemPrivateTemplateID
}
if cid == "" && templateID == "" {
return nil
}
m := map[string]any{}
if cid != "" {
m["channel_id"] = cid
}
if templateID != "" {
m["private_msg_template_id"] = templateID
// private_title_parameters / private_content_parameters 为 JSONObject
m["private_title_parameters"] = map[string]any{
"title": params.Title,
}
m["private_content_parameters"] = map[string]any{
"content": params.Body,
"sender": params.SenderName,
}
}
return m
}