261 lines
8.2 KiB
C#
261 lines
8.2 KiB
C#
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
|
|
};
|
|
}
|
|
}
|
|
|