feat(wechat): Add template field mapping configuration for notification templates
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
- Add field mapping properties to NotificationTemplatesConfig interface for unlock, favorite, message, and daily recommend templates - Add UI form fields in system config view for configuring JSON-based field mappings with helper text - Implement dynamic template data construction using configured field mappings in NotificationService - Add fallback to traditional first/keyword format when field mapping is not configured - Support flexible field mapping with value types: title, name, content, time, remark - Update SystemConfigService to persist and retrieve field mapping configurations - Enable backend-driven template field customization without code changes
This commit is contained in:
parent
125ff21b77
commit
3764b6ef57
|
|
@ -192,9 +192,13 @@ export interface NotificationTemplatesConfig {
|
|||
token?: string
|
||||
encodingAESKey?: string
|
||||
unlockTemplateId?: string
|
||||
unlockFieldMapping?: string
|
||||
favoriteTemplateId?: string
|
||||
favoriteFieldMapping?: string
|
||||
messageTemplateId?: string
|
||||
messageFieldMapping?: string
|
||||
dailyRecommendTemplateId?: string
|
||||
dailyRecommendFieldMapping?: string
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -340,6 +340,15 @@
|
|||
<div class="template-tip">有用户解锁我时,服务号发送相应通知</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="解锁通知字段映射">
|
||||
<el-input
|
||||
v-model="templateForm.unlockFieldMapping"
|
||||
placeholder='例: {"thing1":"title","thing2":"name","time3":"time"}'
|
||||
clearable
|
||||
/>
|
||||
<div class="template-tip">JSON格式,key为模板字段名,value为数据类型:title=标题, name=名称, content=内容, time=时间, remark=备注</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="收藏通知模板ID">
|
||||
<el-input
|
||||
v-model="templateForm.favoriteTemplateId"
|
||||
|
|
@ -349,6 +358,15 @@
|
|||
<div class="template-tip">有用户收藏我时,服务号发送相应通知</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="收藏通知字段映射">
|
||||
<el-input
|
||||
v-model="templateForm.favoriteFieldMapping"
|
||||
placeholder='例: {"thing1":"title","thing2":"name","time3":"time"}'
|
||||
clearable
|
||||
/>
|
||||
<div class="template-tip">JSON格式,key为模板字段名,value为数据类型:title=标题, name=名称, content=内容, time=时间, remark=备注</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="消息通知模板ID">
|
||||
<el-input
|
||||
v-model="templateForm.messageTemplateId"
|
||||
|
|
@ -358,6 +376,15 @@
|
|||
<div class="template-tip">首次沟通通知、5分钟未回复提醒共用此模板</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="消息通知字段映射">
|
||||
<el-input
|
||||
v-model="templateForm.messageFieldMapping"
|
||||
placeholder='例: {"thing1":"title","thing2":"name","time3":"time"}'
|
||||
clearable
|
||||
/>
|
||||
<div class="template-tip">JSON格式,key为模板字段名,value为数据类型:title=标题, name=名称, content=内容, time=时间, remark=备注</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="每日推荐通知模板ID">
|
||||
<el-input
|
||||
v-model="templateForm.dailyRecommendTemplateId"
|
||||
|
|
@ -367,6 +394,15 @@
|
|||
<div class="template-tip">每天早上8~10点随机时间发送推荐更新通知</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="每日推荐字段映射">
|
||||
<el-input
|
||||
v-model="templateForm.dailyRecommendFieldMapping"
|
||||
placeholder='例: {"thing1":"title","thing2":"content","time3":"time"}'
|
||||
clearable
|
||||
/>
|
||||
<div class="template-tip">JSON格式,key为模板字段名,value为数据类型:title=标题, name=名称, content=内容, time=时间, remark=备注</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="saveNotificationTemplates" :loading="savingTemplates">
|
||||
保存模板配置
|
||||
|
|
@ -438,9 +474,13 @@ const templateForm = ref({
|
|||
token: '',
|
||||
encodingAESKey: '',
|
||||
unlockTemplateId: '',
|
||||
unlockFieldMapping: '',
|
||||
favoriteTemplateId: '',
|
||||
favoriteFieldMapping: '',
|
||||
messageTemplateId: '',
|
||||
dailyRecommendTemplateId: ''
|
||||
messageFieldMapping: '',
|
||||
dailyRecommendTemplateId: '',
|
||||
dailyRecommendFieldMapping: ''
|
||||
})
|
||||
|
||||
const saving = ref(false)
|
||||
|
|
@ -718,9 +758,13 @@ const loadNotificationTemplates = async () => {
|
|||
templateForm.value.token = res.token || ''
|
||||
templateForm.value.encodingAESKey = res.encodingAESKey || ''
|
||||
templateForm.value.unlockTemplateId = res.unlockTemplateId || ''
|
||||
templateForm.value.unlockFieldMapping = res.unlockFieldMapping || ''
|
||||
templateForm.value.favoriteTemplateId = res.favoriteTemplateId || ''
|
||||
templateForm.value.favoriteFieldMapping = res.favoriteFieldMapping || ''
|
||||
templateForm.value.messageTemplateId = res.messageTemplateId || ''
|
||||
templateForm.value.messageFieldMapping = res.messageFieldMapping || ''
|
||||
templateForm.value.dailyRecommendTemplateId = res.dailyRecommendTemplateId || ''
|
||||
templateForm.value.dailyRecommendFieldMapping = res.dailyRecommendFieldMapping || ''
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载模板配置失败:', error)
|
||||
|
|
@ -734,9 +778,13 @@ const saveNotificationTemplates = async () => {
|
|||
token: templateForm.value.token || undefined,
|
||||
encodingAESKey: templateForm.value.encodingAESKey || undefined,
|
||||
unlockTemplateId: templateForm.value.unlockTemplateId || undefined,
|
||||
unlockFieldMapping: templateForm.value.unlockFieldMapping || undefined,
|
||||
favoriteTemplateId: templateForm.value.favoriteTemplateId || undefined,
|
||||
favoriteFieldMapping: templateForm.value.favoriteFieldMapping || undefined,
|
||||
messageTemplateId: templateForm.value.messageTemplateId || undefined,
|
||||
dailyRecommendTemplateId: templateForm.value.dailyRecommendTemplateId || undefined
|
||||
messageFieldMapping: templateForm.value.messageFieldMapping || undefined,
|
||||
dailyRecommendTemplateId: templateForm.value.dailyRecommendTemplateId || undefined,
|
||||
dailyRecommendFieldMapping: templateForm.value.dailyRecommendFieldMapping || undefined
|
||||
})
|
||||
ElMessage.success('模板配置保存成功')
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -544,6 +544,43 @@ public class NotificationService : INotificationService
|
|||
return false;
|
||||
}
|
||||
|
||||
// 从数据库读取字段映射
|
||||
var fieldMapping = await GetServiceAccountFieldMappingAsync(templateType);
|
||||
Dictionary<string, TemplateDataItem> data;
|
||||
|
||||
if (fieldMapping != null && fieldMapping.Count > 0)
|
||||
{
|
||||
// 使用后台配置的字段映射动态构建
|
||||
// 映射格式: {"thing1":"title","thing2":"name","time3":"time","thing4":"content","thing5":"remark"}
|
||||
// 值映射: title->标题, name->名称, content->内容, time->时间, remark->备注
|
||||
data = new Dictionary<string, TemplateDataItem>();
|
||||
foreach (var (fieldName, valueKey) in fieldMapping)
|
||||
{
|
||||
var value = valueKey?.ToLower() switch
|
||||
{
|
||||
"title" => title,
|
||||
"name" => name,
|
||||
"content" => content,
|
||||
"time" => time,
|
||||
"remark" => "点击查看详情",
|
||||
_ => valueKey ?? ""
|
||||
};
|
||||
data[fieldName] = new TemplateDataItem { Value = value };
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 兜底:使用传统的 first/keyword 格式
|
||||
data = new Dictionary<string, TemplateDataItem>
|
||||
{
|
||||
["first"] = new TemplateDataItem { Value = title },
|
||||
["keyword1"] = new TemplateDataItem { Value = name },
|
||||
["keyword2"] = new TemplateDataItem { Value = content },
|
||||
["keyword3"] = new TemplateDataItem { Value = time },
|
||||
["remark"] = new TemplateDataItem { Value = "点击查看详情" }
|
||||
};
|
||||
}
|
||||
|
||||
var request = new ServiceAccountTemplateMessageRequest
|
||||
{
|
||||
ToUser = serviceAccountOpenId,
|
||||
|
|
@ -553,14 +590,7 @@ public class NotificationService : INotificationService
|
|||
AppId = _weChatOptions.MiniProgram.AppId,
|
||||
PagePath = page
|
||||
},
|
||||
Data = new Dictionary<string, TemplateDataItem>
|
||||
{
|
||||
["first"] = new TemplateDataItem { Value = title },
|
||||
["keyword1"] = new TemplateDataItem { Value = name },
|
||||
["keyword2"] = new TemplateDataItem { Value = content },
|
||||
["keyword3"] = new TemplateDataItem { Value = time },
|
||||
["remark"] = new TemplateDataItem { Value = "点击查看详情" }
|
||||
}
|
||||
Data = data
|
||||
};
|
||||
|
||||
var success = await _weChatService.SendServiceAccountTemplateMessageAsync(request);
|
||||
|
|
@ -576,6 +606,37 @@ public class NotificationService : INotificationService
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取服务号模板字段映射
|
||||
/// </summary>
|
||||
private async Task<Dictionary<string, string>?> GetServiceAccountFieldMappingAsync(NotificationTemplateType templateType)
|
||||
{
|
||||
string? configKey = templateType switch
|
||||
{
|
||||
NotificationTemplateType.Unlock => SystemConfigService.SaUnlockFieldMappingKey,
|
||||
NotificationTemplateType.Favorite => SystemConfigService.SaFavoriteFieldMappingKey,
|
||||
NotificationTemplateType.FirstMessage => SystemConfigService.SaMessageFieldMappingKey,
|
||||
NotificationTemplateType.MessageReminder => SystemConfigService.SaMessageFieldMappingKey,
|
||||
NotificationTemplateType.DailyRecommend => SystemConfigService.SaDailyRecommendFieldMappingKey,
|
||||
_ => null
|
||||
};
|
||||
|
||||
if (configKey == null) return null;
|
||||
|
||||
var json = await _configService.GetConfigValueAsync(configKey);
|
||||
if (string.IsNullOrEmpty(json)) return null;
|
||||
|
||||
try
|
||||
{
|
||||
return System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, string>>(json);
|
||||
}
|
||||
catch
|
||||
{
|
||||
_logger.LogWarning("解析字段映射失败: Key={Key}, Value={Value}", configKey, json);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -101,9 +101,13 @@ public class SystemConfigService : ISystemConfigService
|
|||
public const string SaTokenKey = "sa_token";
|
||||
public const string SaEncodingAesKeyKey = "sa_encoding_aes_key";
|
||||
public const string SaUnlockTemplateIdKey = "sa_unlock_template_id";
|
||||
public const string SaUnlockFieldMappingKey = "sa_unlock_field_mapping";
|
||||
public const string SaFavoriteTemplateIdKey = "sa_favorite_template_id";
|
||||
public const string SaFavoriteFieldMappingKey = "sa_favorite_field_mapping";
|
||||
public const string SaMessageTemplateIdKey = "sa_message_template_id";
|
||||
public const string SaMessageFieldMappingKey = "sa_message_field_mapping";
|
||||
public const string SaDailyRecommendTemplateIdKey = "sa_daily_recommend_template_id";
|
||||
public const string SaDailyRecommendFieldMappingKey = "sa_daily_recommend_field_mapping";
|
||||
|
||||
public SystemConfigService(
|
||||
IRepository<SystemConfig> configRepository,
|
||||
|
|
@ -360,9 +364,13 @@ public class SystemConfigService : ISystemConfigService
|
|||
Token = await GetConfigValueAsync(SaTokenKey),
|
||||
EncodingAESKey = await GetConfigValueAsync(SaEncodingAesKeyKey),
|
||||
UnlockTemplateId = await GetConfigValueAsync(SaUnlockTemplateIdKey),
|
||||
UnlockFieldMapping = await GetConfigValueAsync(SaUnlockFieldMappingKey),
|
||||
FavoriteTemplateId = await GetConfigValueAsync(SaFavoriteTemplateIdKey),
|
||||
FavoriteFieldMapping = await GetConfigValueAsync(SaFavoriteFieldMappingKey),
|
||||
MessageTemplateId = await GetConfigValueAsync(SaMessageTemplateIdKey),
|
||||
DailyRecommendTemplateId = await GetConfigValueAsync(SaDailyRecommendTemplateIdKey)
|
||||
MessageFieldMapping = await GetConfigValueAsync(SaMessageFieldMappingKey),
|
||||
DailyRecommendTemplateId = await GetConfigValueAsync(SaDailyRecommendTemplateIdKey),
|
||||
DailyRecommendFieldMapping = await GetConfigValueAsync(SaDailyRecommendFieldMappingKey)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -377,12 +385,20 @@ public class SystemConfigService : ISystemConfigService
|
|||
await SetConfigValueAsync(SaEncodingAesKeyKey, templates.EncodingAESKey, "服务号消息加解密密钥");
|
||||
if (!string.IsNullOrEmpty(templates.UnlockTemplateId))
|
||||
await SetConfigValueAsync(SaUnlockTemplateIdKey, templates.UnlockTemplateId, "服务号解锁通知模板ID");
|
||||
if (!string.IsNullOrEmpty(templates.UnlockFieldMapping))
|
||||
await SetConfigValueAsync(SaUnlockFieldMappingKey, templates.UnlockFieldMapping, "服务号解锁通知字段映射");
|
||||
if (!string.IsNullOrEmpty(templates.FavoriteTemplateId))
|
||||
await SetConfigValueAsync(SaFavoriteTemplateIdKey, templates.FavoriteTemplateId, "服务号收藏通知模板ID");
|
||||
if (!string.IsNullOrEmpty(templates.FavoriteFieldMapping))
|
||||
await SetConfigValueAsync(SaFavoriteFieldMappingKey, templates.FavoriteFieldMapping, "服务号收藏通知字段映射");
|
||||
if (!string.IsNullOrEmpty(templates.MessageTemplateId))
|
||||
await SetConfigValueAsync(SaMessageTemplateIdKey, templates.MessageTemplateId, "服务号消息通知模板ID");
|
||||
if (!string.IsNullOrEmpty(templates.MessageFieldMapping))
|
||||
await SetConfigValueAsync(SaMessageFieldMappingKey, templates.MessageFieldMapping, "服务号消息通知字段映射");
|
||||
if (!string.IsNullOrEmpty(templates.DailyRecommendTemplateId))
|
||||
await SetConfigValueAsync(SaDailyRecommendTemplateIdKey, templates.DailyRecommendTemplateId, "服务号每日推荐通知模板ID");
|
||||
if (!string.IsNullOrEmpty(templates.DailyRecommendFieldMapping))
|
||||
await SetConfigValueAsync(SaDailyRecommendFieldMappingKey, templates.DailyRecommendFieldMapping, "服务号每日推荐通知字段映射");
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -439,18 +455,38 @@ public class NotificationTemplatesDto
|
|||
/// </summary>
|
||||
public string? UnlockTemplateId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 解锁通知字段映射JSON,如 {"thing1":"title","thing2":"name","time3":"time"}
|
||||
/// </summary>
|
||||
public string? UnlockFieldMapping { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 收藏通知模板ID
|
||||
/// </summary>
|
||||
public string? FavoriteTemplateId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 收藏通知字段映射JSON
|
||||
/// </summary>
|
||||
public string? FavoriteFieldMapping { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 消息通知模板ID(首次消息/未回复提醒共用)
|
||||
/// </summary>
|
||||
public string? MessageTemplateId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 消息通知字段映射JSON
|
||||
/// </summary>
|
||||
public string? MessageFieldMapping { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 每日推荐通知模板ID
|
||||
/// </summary>
|
||||
public string? DailyRecommendTemplateId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 每日推荐通知字段映射JSON
|
||||
/// </summary>
|
||||
public string? DailyRecommendFieldMapping { get; set; }
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user