feat(marketing): implement marketing calendar backend
All checks were successful
Build and Deploy TenantApi + SkuWorker / build-and-deploy (push) Successful in 1m59s

This commit is contained in:
2026-03-03 10:10:41 +08:00
parent decfa4fa12
commit 1b28fa6db4
8 changed files with 1891 additions and 1 deletions

View File

@@ -0,0 +1,397 @@
namespace TakeoutSaaS.Application.App.Coupons.Calendar.Dto;
/// <summary>
/// 营销日历总览。
/// </summary>
public sealed class MarketingCalendarOverviewDto
{
/// <summary>
/// 月份标识yyyy-MM
/// </summary>
public string Month { get; init; } = string.Empty;
/// <summary>
/// 年份。
/// </summary>
public int Year { get; init; }
/// <summary>
/// 月份1-12
/// </summary>
public int MonthValue { get; init; }
/// <summary>
/// 月初日期UTC
/// </summary>
public DateTime MonthStartDate { get; init; }
/// <summary>
/// 月末日期UTC
/// </summary>
public DateTime MonthEndDate { get; init; }
/// <summary>
/// 今天所在日(不在当月则为 0
/// </summary>
public int TodayDay { get; init; }
/// <summary>
/// 日期头列表。
/// </summary>
public IReadOnlyList<MarketingCalendarDayDto> Days { get; init; } = [];
/// <summary>
/// 图例。
/// </summary>
public IReadOnlyList<MarketingCalendarLegendDto> Legends { get; init; } = [];
/// <summary>
/// 顶部统计。
/// </summary>
public MarketingCalendarStatsDto Stats { get; init; } = new();
/// <summary>
/// 冲突横幅。
/// </summary>
public MarketingCalendarConflictBannerDto? ConflictBanner { get; init; }
/// <summary>
/// 冲突区间。
/// </summary>
public IReadOnlyList<MarketingCalendarConflictDto> Conflicts { get; init; } = [];
/// <summary>
/// 活动列表。
/// </summary>
public IReadOnlyList<MarketingCalendarActivityDto> Activities { get; init; } = [];
}
/// <summary>
/// 日期头。
/// </summary>
public sealed class MarketingCalendarDayDto
{
/// <summary>
/// 日1-31
/// </summary>
public int Day { get; init; }
/// <summary>
/// 是否周末。
/// </summary>
public bool IsWeekend { get; init; }
/// <summary>
/// 是否今日。
/// </summary>
public bool IsToday { get; init; }
}
/// <summary>
/// 图例。
/// </summary>
public sealed class MarketingCalendarLegendDto
{
/// <summary>
/// 图例类型。
/// </summary>
public string Type { get; init; } = string.Empty;
/// <summary>
/// 图例名称。
/// </summary>
public string Label { get; init; } = string.Empty;
/// <summary>
/// 图例颜色。
/// </summary>
public string Color { get; init; } = string.Empty;
}
/// <summary>
/// 顶部统计。
/// </summary>
public sealed class MarketingCalendarStatsDto
{
/// <summary>
/// 本月活动数。
/// </summary>
public int TotalActivityCount { get; init; }
/// <summary>
/// 进行中活动数。
/// </summary>
public int OngoingCount { get; init; }
/// <summary>
/// 最大并行活动数。
/// </summary>
public int MaxConcurrentCount { get; init; }
/// <summary>
/// 本月预计优惠金额。
/// </summary>
public decimal EstimatedDiscountAmount { get; init; }
}
/// <summary>
/// 活动。
/// </summary>
public sealed class MarketingCalendarActivityDto
{
/// <summary>
/// 活动唯一键(跨模块)。
/// </summary>
public string ActivityId { get; init; } = string.Empty;
/// <summary>
/// 来源模块full_reduction/flash_sale/seckill/coupon/punch_card
/// </summary>
public string SourceType { get; init; } = string.Empty;
/// <summary>
/// 来源标识。
/// </summary>
public string SourceId { get; init; } = string.Empty;
/// <summary>
/// 日历类型reduce/gift/second_half/flash_sale/seckill/coupon/punch_card
/// </summary>
public string CalendarType { get; init; } = string.Empty;
/// <summary>
/// 活动名称。
/// </summary>
public string Name { get; init; } = string.Empty;
/// <summary>
/// 活动颜色。
/// </summary>
public string Color { get; init; } = string.Empty;
/// <summary>
/// 活动摘要。
/// </summary>
public string Summary { get; init; } = string.Empty;
/// <summary>
/// 展示状态ongoing/upcoming/ended/disabled
/// </summary>
public string DisplayStatus { get; init; } = string.Empty;
/// <summary>
/// 是否弱化。
/// </summary>
public bool IsDimmed { get; init; }
/// <summary>
/// 活动开始日期UTC
/// </summary>
public DateTime StartDate { get; init; }
/// <summary>
/// 活动结束日期UTC
/// </summary>
public DateTime EndDate { get; init; }
/// <summary>
/// 预计优惠金额。
/// </summary>
public decimal EstimatedDiscountAmount { get; init; }
/// <summary>
/// 活动条。
/// </summary>
public IReadOnlyList<MarketingCalendarActivityBarDto> Bars { get; init; } = [];
/// <summary>
/// 二级抽屉详情。
/// </summary>
public MarketingCalendarActivityDetailDto Detail { get; init; } = new();
}
/// <summary>
/// 活动条。
/// </summary>
public sealed class MarketingCalendarActivityBarDto
{
/// <summary>
/// 条标识。
/// </summary>
public string BarId { get; init; } = string.Empty;
/// <summary>
/// 开始日1-31
/// </summary>
public int StartDay { get; init; }
/// <summary>
/// 结束日1-31
/// </summary>
public int EndDay { get; init; }
/// <summary>
/// 条文案。
/// </summary>
public string Label { get; init; } = string.Empty;
/// <summary>
/// 是否里程碑。
/// </summary>
public bool IsMilestone { get; init; }
/// <summary>
/// 是否弱化。
/// </summary>
public bool IsDimmed { get; init; }
}
/// <summary>
/// 活动详情。
/// </summary>
public sealed class MarketingCalendarActivityDetailDto
{
/// <summary>
/// 模块名称。
/// </summary>
public string ModuleName { get; init; } = string.Empty;
/// <summary>
/// 详情描述。
/// </summary>
public string Description { get; init; } = string.Empty;
/// <summary>
/// 明细字段。
/// </summary>
public IReadOnlyList<MarketingCalendarDetailFieldDto> Fields { get; init; } = [];
}
/// <summary>
/// 详情字段。
/// </summary>
public sealed class MarketingCalendarDetailFieldDto
{
/// <summary>
/// 标签。
/// </summary>
public string Label { get; init; } = string.Empty;
/// <summary>
/// 值。
/// </summary>
public string Value { get; init; } = string.Empty;
}
/// <summary>
/// 冲突横幅。
/// </summary>
public sealed class MarketingCalendarConflictBannerDto
{
/// <summary>
/// 冲突标识。
/// </summary>
public string ConflictId { get; init; } = string.Empty;
/// <summary>
/// 开始日。
/// </summary>
public int StartDay { get; init; }
/// <summary>
/// 结束日。
/// </summary>
public int EndDay { get; init; }
/// <summary>
/// 同时进行活动数。
/// </summary>
public int ActivityCount { get; init; }
/// <summary>
/// 最大并行活动数。
/// </summary>
public int MaxConcurrentCount { get; init; }
/// <summary>
/// 冲突区间数。
/// </summary>
public int ConflictCount { get; init; }
}
/// <summary>
/// 冲突区间。
/// </summary>
public sealed class MarketingCalendarConflictDto
{
/// <summary>
/// 冲突标识。
/// </summary>
public string ConflictId { get; init; } = string.Empty;
/// <summary>
/// 开始日。
/// </summary>
public int StartDay { get; init; }
/// <summary>
/// 结束日。
/// </summary>
public int EndDay { get; init; }
/// <summary>
/// 同时进行活动数。
/// </summary>
public int ActivityCount { get; init; }
/// <summary>
/// 最大并行活动数。
/// </summary>
public int MaxConcurrentCount { get; init; }
/// <summary>
/// 活动标识集合。
/// </summary>
public IReadOnlyList<string> ActivityIds { get; init; } = [];
/// <summary>
/// 冲突活动摘要。
/// </summary>
public IReadOnlyList<MarketingCalendarConflictActivityDto> Activities { get; init; } = [];
}
/// <summary>
/// 冲突活动摘要。
/// </summary>
public sealed class MarketingCalendarConflictActivityDto
{
/// <summary>
/// 活动唯一键。
/// </summary>
public string ActivityId { get; init; } = string.Empty;
/// <summary>
/// 日历类型。
/// </summary>
public string CalendarType { get; init; } = string.Empty;
/// <summary>
/// 活动名称。
/// </summary>
public string Name { get; init; } = string.Empty;
/// <summary>
/// 活动摘要。
/// </summary>
public string Summary { get; init; } = string.Empty;
/// <summary>
/// 活动颜色。
/// </summary>
public string Color { get; init; } = string.Empty;
/// <summary>
/// 展示状态。
/// </summary>
public string DisplayStatus { get; init; } = string.Empty;
}

View File

@@ -0,0 +1,25 @@
using MediatR;
using TakeoutSaaS.Application.App.Coupons.Calendar.Dto;
namespace TakeoutSaaS.Application.App.Coupons.Calendar.Queries;
/// <summary>
/// 查询营销日历总览。
/// </summary>
public sealed class GetMarketingCalendarOverviewQuery : IRequest<MarketingCalendarOverviewDto>
{
/// <summary>
/// 门店 ID。
/// </summary>
public long StoreId { get; init; }
/// <summary>
/// 年份。
/// </summary>
public int Year { get; init; }
/// <summary>
/// 月份1-12
/// </summary>
public int Month { get; init; }
}