feat: 完成会员消息触达后端模块
This commit is contained in:
@@ -0,0 +1,260 @@
|
||||
using System.Text.Json;
|
||||
using TakeoutSaaS.Application.App.Members.MessageReach.Dto;
|
||||
using TakeoutSaaS.Domain.Membership.Entities;
|
||||
using TakeoutSaaS.Domain.Membership.Enums;
|
||||
using TakeoutSaaS.Shared.Abstractions.Constants;
|
||||
using TakeoutSaaS.Shared.Abstractions.Exceptions;
|
||||
|
||||
namespace TakeoutSaaS.Application.App.Members.MessageReach;
|
||||
|
||||
internal static class MemberMessageReachMapping
|
||||
{
|
||||
internal static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web)
|
||||
{
|
||||
PropertyNameCaseInsensitive = true
|
||||
};
|
||||
|
||||
internal static MemberMessageAudienceType ParseAudienceType(string? value)
|
||||
{
|
||||
return (value ?? string.Empty).Trim().ToLowerInvariant() switch
|
||||
{
|
||||
"all" => MemberMessageAudienceType.All,
|
||||
"tag" => MemberMessageAudienceType.Tags,
|
||||
"tags" => MemberMessageAudienceType.Tags,
|
||||
_ => throw new BusinessException(ErrorCodes.BadRequest, "audienceType 非法")
|
||||
};
|
||||
}
|
||||
|
||||
internal static string ToAudienceTypeText(MemberMessageAudienceType value)
|
||||
{
|
||||
return value switch
|
||||
{
|
||||
MemberMessageAudienceType.All => "all",
|
||||
MemberMessageAudienceType.Tags => "tag",
|
||||
_ => "all"
|
||||
};
|
||||
}
|
||||
|
||||
internal static MemberMessageScheduleType ParseScheduleType(string? value)
|
||||
{
|
||||
return (value ?? string.Empty).Trim().ToLowerInvariant() switch
|
||||
{
|
||||
"immediate" => MemberMessageScheduleType.Immediate,
|
||||
"scheduled" => MemberMessageScheduleType.Scheduled,
|
||||
_ => throw new BusinessException(ErrorCodes.BadRequest, "scheduleType 非法")
|
||||
};
|
||||
}
|
||||
|
||||
internal static string ToScheduleTypeText(MemberMessageScheduleType value)
|
||||
{
|
||||
return value switch
|
||||
{
|
||||
MemberMessageScheduleType.Immediate => "immediate",
|
||||
MemberMessageScheduleType.Scheduled => "scheduled",
|
||||
_ => "immediate"
|
||||
};
|
||||
}
|
||||
|
||||
internal static MemberMessageStatus ParseStatusOrNull(string? value)
|
||||
{
|
||||
return (value ?? string.Empty).Trim().ToLowerInvariant() switch
|
||||
{
|
||||
"draft" => MemberMessageStatus.Draft,
|
||||
"pending" => MemberMessageStatus.Pending,
|
||||
"sending" => MemberMessageStatus.Sending,
|
||||
"sent" => MemberMessageStatus.Sent,
|
||||
"failed" => MemberMessageStatus.Failed,
|
||||
_ => throw new BusinessException(ErrorCodes.BadRequest, "status 非法")
|
||||
};
|
||||
}
|
||||
|
||||
internal static MemberMessageStatus? TryParseStatus(string? value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return ParseStatusOrNull(value);
|
||||
}
|
||||
|
||||
internal static string ToStatusText(MemberMessageStatus value)
|
||||
{
|
||||
return value switch
|
||||
{
|
||||
MemberMessageStatus.Draft => "draft",
|
||||
MemberMessageStatus.Pending => "pending",
|
||||
MemberMessageStatus.Sending => "sending",
|
||||
MemberMessageStatus.Sent => "sent",
|
||||
MemberMessageStatus.Failed => "failed",
|
||||
_ => "draft"
|
||||
};
|
||||
}
|
||||
|
||||
internal static MemberMessageChannel ParseChannel(string? value)
|
||||
{
|
||||
return (value ?? string.Empty).Trim().ToLowerInvariant() switch
|
||||
{
|
||||
"inapp" => MemberMessageChannel.InApp,
|
||||
"sms" => MemberMessageChannel.Sms,
|
||||
"wechat-mini" => MemberMessageChannel.WeChatMini,
|
||||
"wechat" => MemberMessageChannel.WeChatMini,
|
||||
_ => throw new BusinessException(ErrorCodes.BadRequest, "channel 非法")
|
||||
};
|
||||
}
|
||||
|
||||
internal static MemberMessageChannel? TryParseChannel(string? value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return ParseChannel(value);
|
||||
}
|
||||
|
||||
internal static string ToChannelText(MemberMessageChannel value)
|
||||
{
|
||||
return value switch
|
||||
{
|
||||
MemberMessageChannel.InApp => "inapp",
|
||||
MemberMessageChannel.Sms => "sms",
|
||||
MemberMessageChannel.WeChatMini => "wechat-mini",
|
||||
_ => "inapp"
|
||||
};
|
||||
}
|
||||
|
||||
internal static string ToRecipientStatusText(MemberMessageRecipientStatus value)
|
||||
{
|
||||
return value switch
|
||||
{
|
||||
MemberMessageRecipientStatus.Pending => "pending",
|
||||
MemberMessageRecipientStatus.Sent => "sent",
|
||||
MemberMessageRecipientStatus.Failed => "failed",
|
||||
_ => "pending"
|
||||
};
|
||||
}
|
||||
|
||||
internal static MemberMessageTemplateCategory ParseTemplateCategory(string? value)
|
||||
{
|
||||
return (value ?? string.Empty).Trim().ToLowerInvariant() switch
|
||||
{
|
||||
"marketing" => MemberMessageTemplateCategory.Marketing,
|
||||
"notice" => MemberMessageTemplateCategory.Notice,
|
||||
"recall" => MemberMessageTemplateCategory.Recall,
|
||||
_ => throw new BusinessException(ErrorCodes.BadRequest, "category 非法")
|
||||
};
|
||||
}
|
||||
|
||||
internal static MemberMessageTemplateCategory? TryParseTemplateCategory(string? value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return ParseTemplateCategory(value);
|
||||
}
|
||||
|
||||
internal static string ToTemplateCategoryText(MemberMessageTemplateCategory value)
|
||||
{
|
||||
return value switch
|
||||
{
|
||||
MemberMessageTemplateCategory.Marketing => "marketing",
|
||||
MemberMessageTemplateCategory.Notice => "notice",
|
||||
MemberMessageTemplateCategory.Recall => "recall",
|
||||
_ => "notice"
|
||||
};
|
||||
}
|
||||
|
||||
internal static IReadOnlyList<string> NormalizeTags(IReadOnlyList<string>? tags)
|
||||
{
|
||||
return (tags ?? [])
|
||||
.Select(item => (item ?? string.Empty).Trim())
|
||||
.Where(item => !string.IsNullOrWhiteSpace(item))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.OrderBy(item => item, StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
internal static IReadOnlyList<string> NormalizeChannels(IReadOnlyList<string>? channels)
|
||||
{
|
||||
var parsed = (channels ?? [])
|
||||
.Select(ParseChannel)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
if (parsed.Count == 0)
|
||||
{
|
||||
throw new BusinessException(ErrorCodes.BadRequest, "channels 不能为空");
|
||||
}
|
||||
|
||||
return parsed.Select(ToChannelText).ToList();
|
||||
}
|
||||
|
||||
internal static string SerializeStringArray(IReadOnlyList<string> source)
|
||||
{
|
||||
return JsonSerializer.Serialize(source, JsonOptions);
|
||||
}
|
||||
|
||||
internal static IReadOnlyList<string> DeserializeStringArray(string? value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return JsonSerializer.Deserialize<List<string>>(value, JsonOptions)?
|
||||
.Select(item => (item ?? string.Empty).Trim())
|
||||
.Where(item => !string.IsNullOrWhiteSpace(item))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList() ?? [];
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
internal static decimal ResolveRatePercent(int numerator, int denominator)
|
||||
{
|
||||
if (denominator <= 0 || numerator <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return decimal.Round((decimal)numerator * 100m / denominator, 2, MidpointRounding.AwayFromZero);
|
||||
}
|
||||
|
||||
internal static MemberMessageTemplateDto ToTemplateDto(MemberMessageTemplate source)
|
||||
{
|
||||
return new MemberMessageTemplateDto
|
||||
{
|
||||
TemplateId = source.Id,
|
||||
Name = source.Name,
|
||||
Category = ToTemplateCategoryText(source.Category),
|
||||
Content = source.Content,
|
||||
UsageCount = source.UsageCount,
|
||||
LastUsedAt = source.LastUsedAt
|
||||
};
|
||||
}
|
||||
|
||||
internal static MemberMessageReachRecipientDto ToRecipientDto(MemberReachRecipient source)
|
||||
{
|
||||
return new MemberMessageReachRecipientDto
|
||||
{
|
||||
MemberId = source.MemberId,
|
||||
Channel = ToChannelText(source.Channel),
|
||||
Status = ToRecipientStatusText(source.Status),
|
||||
Mobile = source.Mobile,
|
||||
OpenId = source.OpenId,
|
||||
SentAt = source.SentAt,
|
||||
ReadAt = source.ReadAt,
|
||||
ConvertedAt = source.ConvertedAt,
|
||||
ErrorMessage = source.ErrorMessage
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user