diff --git a/TakeoutSaaS.Docs b/TakeoutSaaS.Docs
index d7d55e9..9006c8a 160000
--- a/TakeoutSaaS.Docs
+++ b/TakeoutSaaS.Docs
@@ -1 +1 @@
-Subproject commit d7d55e990836f82ef7de91af02ae2f3a3a02b71b
+Subproject commit 9006c8a58995d4adddab28599193c3631935ee43
diff --git a/src/Api/TakeoutSaaS.TenantApi/Contracts/Member/MemberListContracts.cs b/src/Api/TakeoutSaaS.TenantApi/Contracts/Member/MemberListContracts.cs
new file mode 100644
index 0000000..f21cc4e
--- /dev/null
+++ b/src/Api/TakeoutSaaS.TenantApi/Contracts/Member/MemberListContracts.cs
@@ -0,0 +1,346 @@
+namespace TakeoutSaaS.TenantApi.Contracts.Member;
+
+///
+/// 会员列表筛选请求。
+///
+public class MemberListFilterRequest
+{
+ ///
+ /// 门店 ID(可选,未传表示当前商户全部可见门店)。
+ ///
+ public string? StoreId { get; set; }
+
+ ///
+ /// 关键词(姓名/手机号)。
+ ///
+ public string? Keyword { get; set; }
+
+ ///
+ /// 等级标识。
+ ///
+ public string? TierId { get; set; }
+}
+
+///
+/// 会员列表分页请求。
+///
+public sealed class MemberListRequest : MemberListFilterRequest
+{
+ ///
+ /// 页码。
+ ///
+ public int Page { get; set; } = 1;
+
+ ///
+ /// 每页条数。
+ ///
+ public int PageSize { get; set; } = 10;
+}
+
+///
+/// 会员详情请求。
+///
+public sealed class MemberDetailRequest
+{
+ ///
+ /// 门店 ID(可选,未传表示当前商户全部可见门店)。
+ ///
+ public string? StoreId { get; set; }
+
+ ///
+ /// 会员标识。
+ ///
+ public string MemberId { get; set; } = string.Empty;
+}
+
+///
+/// 保存会员标签请求。
+///
+public sealed class SaveMemberTagsRequest
+{
+ ///
+ /// 门店 ID(可选,未传表示当前商户全部可见门店)。
+ ///
+ public string? StoreId { get; set; }
+
+ ///
+ /// 会员标识。
+ ///
+ public string MemberId { get; set; } = string.Empty;
+
+ ///
+ /// 标签集合。
+ ///
+ public List Tags { get; set; } = [];
+}
+
+///
+/// 会员列表行响应。
+///
+public sealed class MemberListItemResponse
+{
+ ///
+ /// 会员标识。
+ ///
+ public string MemberId { get; set; } = string.Empty;
+
+ ///
+ /// 会员名称。
+ ///
+ public string Name { get; set; } = string.Empty;
+
+ ///
+ /// 头像文案。
+ ///
+ public string AvatarText { get; set; } = string.Empty;
+
+ ///
+ /// 头像颜色。
+ ///
+ public string AvatarColor { get; set; } = string.Empty;
+
+ ///
+ /// 手机号(脱敏)。
+ ///
+ public string MobileMasked { get; set; } = string.Empty;
+
+ ///
+ /// 会员等级标识。
+ ///
+ public string? TierId { get; set; }
+
+ ///
+ /// 会员等级名称。
+ ///
+ public string TierName { get; set; } = string.Empty;
+
+ ///
+ /// 等级主题色。
+ ///
+ public string TierColorHex { get; set; } = string.Empty;
+
+ ///
+ /// 累计消费。
+ ///
+ public decimal TotalAmount { get; set; }
+
+ ///
+ /// 消费次数。
+ ///
+ public int OrderCount { get; set; }
+
+ ///
+ /// 最近消费时间(yyyy-MM-dd)。
+ ///
+ public string LastOrderAt { get; set; } = string.Empty;
+
+ ///
+ /// 储值余额。
+ ///
+ public decimal StoredBalance { get; set; }
+
+ ///
+ /// 积分余额。
+ ///
+ public int PointsBalance { get; set; }
+
+ ///
+ /// 是否沉睡会员。
+ ///
+ public bool IsDormant { get; set; }
+}
+
+///
+/// 会员列表响应。
+///
+public sealed class MemberListResultResponse
+{
+ ///
+ /// 列表项。
+ ///
+ public List Items { get; set; } = [];
+
+ ///
+ /// 总数。
+ ///
+ public int Total { get; set; }
+
+ ///
+ /// 页码。
+ ///
+ public int Page { get; set; }
+
+ ///
+ /// 每页条数。
+ ///
+ public int PageSize { get; set; }
+}
+
+///
+/// 会员列表统计响应。
+///
+public sealed class MemberListStatsResponse
+{
+ ///
+ /// 会员总数。
+ ///
+ public int TotalMembers { get; set; }
+
+ ///
+ /// 本月新增会员数。
+ ///
+ public int MonthlyNewMembers { get; set; }
+
+ ///
+ /// 活跃会员数。
+ ///
+ public int ActiveMembers { get; set; }
+
+ ///
+ /// 沉睡会员数。
+ ///
+ public int DormantMembers { get; set; }
+}
+
+///
+/// 会员最近订单响应。
+///
+public sealed class MemberRecentOrderResponse
+{
+ ///
+ /// 下单日期(yyyy-MM-dd)。
+ ///
+ public string OrderedAt { get; set; } = string.Empty;
+
+ ///
+ /// 订单号。
+ ///
+ public string OrderNo { get; set; } = string.Empty;
+
+ ///
+ /// 订单金额。
+ ///
+ public decimal Amount { get; set; }
+
+ ///
+ /// 订单状态文案。
+ ///
+ public string StatusText { get; set; } = string.Empty;
+}
+
+///
+/// 会员详情响应。
+///
+public sealed class MemberDetailResponse
+{
+ ///
+ /// 会员标识。
+ ///
+ public string MemberId { get; set; } = string.Empty;
+
+ ///
+ /// 会员名称。
+ ///
+ public string Name { get; set; } = string.Empty;
+
+ ///
+ /// 头像文案。
+ ///
+ public string AvatarText { get; set; } = string.Empty;
+
+ ///
+ /// 头像颜色。
+ ///
+ public string AvatarColor { get; set; } = string.Empty;
+
+ ///
+ /// 手机号(脱敏)。
+ ///
+ public string MobileMasked { get; set; } = string.Empty;
+
+ ///
+ /// 注册时间(yyyy-MM-dd)。
+ ///
+ public string JoinedAt { get; set; } = string.Empty;
+
+ ///
+ /// 会员等级标识。
+ ///
+ public string? TierId { get; set; }
+
+ ///
+ /// 会员等级名称。
+ ///
+ public string TierName { get; set; } = string.Empty;
+
+ ///
+ /// 等级主题色。
+ ///
+ public string TierColorHex { get; set; } = string.Empty;
+
+ ///
+ /// 累计消费。
+ ///
+ public decimal TotalAmount { get; set; }
+
+ ///
+ /// 消费次数。
+ ///
+ public int OrderCount { get; set; }
+
+ ///
+ /// 平均客单价。
+ ///
+ public decimal AverageAmount { get; set; }
+
+ ///
+ /// 储值余额。
+ ///
+ public decimal StoredBalance { get; set; }
+
+ ///
+ /// 储值实充余额。
+ ///
+ public decimal StoredRechargeBalance { get; set; }
+
+ ///
+ /// 储值赠金余额。
+ ///
+ public decimal StoredGiftBalance { get; set; }
+
+ ///
+ /// 积分余额。
+ ///
+ public int PointsBalance { get; set; }
+
+ ///
+ /// 会员标签。
+ ///
+ public List Tags { get; set; } = [];
+
+ ///
+ /// 最近订单。
+ ///
+ public List RecentOrders { get; set; } = [];
+}
+
+///
+/// 会员导出响应。
+///
+public sealed class MemberExportResponse
+{
+ ///
+ /// 文件名。
+ ///
+ public string FileName { get; set; } = string.Empty;
+
+ ///
+ /// 文件内容 Base64。
+ ///
+ public string FileContentBase64 { get; set; } = string.Empty;
+
+ ///
+ /// 导出总数。
+ ///
+ public int TotalCount { get; set; }
+}
diff --git a/src/Api/TakeoutSaaS.TenantApi/Contracts/Member/MemberTierContracts.cs b/src/Api/TakeoutSaaS.TenantApi/Contracts/Member/MemberTierContracts.cs
new file mode 100644
index 0000000..e8698ef
--- /dev/null
+++ b/src/Api/TakeoutSaaS.TenantApi/Contracts/Member/MemberTierContracts.cs
@@ -0,0 +1,427 @@
+namespace TakeoutSaaS.TenantApi.Contracts.Member;
+
+///
+/// 会员等级列表项响应。
+///
+public sealed class MemberTierListItemResponse
+{
+ ///
+ /// 等级标识。
+ ///
+ public string TierId { get; set; } = string.Empty;
+
+ ///
+ /// 排序序号。
+ ///
+ public int SortOrder { get; set; }
+
+ ///
+ /// 等级名称。
+ ///
+ public string Name { get; set; } = string.Empty;
+
+ ///
+ /// 图标键。
+ ///
+ public string IconKey { get; set; } = string.Empty;
+
+ ///
+ /// 主题色。
+ ///
+ public string ColorHex { get; set; } = string.Empty;
+
+ ///
+ /// 升级条件文案。
+ ///
+ public string ConditionText { get; set; } = string.Empty;
+
+ ///
+ /// 权益摘要。
+ ///
+ public List Perks { get; set; } = [];
+
+ ///
+ /// 等级会员数。
+ ///
+ public int MemberCount { get; set; }
+
+ ///
+ /// 是否默认等级。
+ ///
+ public bool IsDefault { get; set; }
+
+ ///
+ /// 是否可删除。
+ ///
+ public bool CanDelete { get; set; }
+}
+
+///
+/// 等级详情查询请求。
+///
+public sealed class MemberTierDetailRequest
+{
+ ///
+ /// 等级标识。
+ ///
+ public string? TierId { get; set; }
+}
+
+///
+/// 等级规则响应。
+///
+public sealed class MemberTierRuleResponse
+{
+ ///
+ /// 升级规则类型。
+ ///
+ public string UpgradeRuleType { get; set; } = "none";
+
+ ///
+ /// 升级累计消费门槛。
+ ///
+ public decimal? UpgradeAmountThreshold { get; set; }
+
+ ///
+ /// 升级消费次数门槛。
+ ///
+ public int? UpgradeOrderCountThreshold { get; set; }
+
+ ///
+ /// 降级观察窗口天数。
+ ///
+ public int DowngradeWindowDays { get; set; }
+}
+
+///
+/// 折扣权益响应。
+///
+public sealed class MemberTierDiscountBenefitResponse
+{
+ ///
+ /// 是否启用。
+ ///
+ public bool Enabled { get; set; }
+
+ ///
+ /// 折扣值。
+ ///
+ public decimal? DiscountRate { get; set; }
+}
+
+///
+/// 积分倍率权益响应。
+///
+public sealed class MemberTierPointMultiplierBenefitResponse
+{
+ ///
+ /// 是否启用。
+ ///
+ public bool Enabled { get; set; }
+
+ ///
+ /// 倍率。
+ ///
+ public decimal? Multiplier { get; set; }
+}
+
+///
+/// 生日特权响应。
+///
+public sealed class MemberTierBirthdayBenefitResponse
+{
+ ///
+ /// 是否启用。
+ ///
+ public bool Enabled { get; set; }
+
+ ///
+ /// 是否双倍积分。
+ ///
+ public bool DoublePointsEnabled { get; set; }
+
+ ///
+ /// 券模板 ID。
+ ///
+ public List CouponTemplateIds { get; set; } = [];
+}
+
+///
+/// 每月赠券响应。
+///
+public sealed class MemberTierMonthlyCouponBenefitResponse
+{
+ ///
+ /// 是否启用。
+ ///
+ public bool Enabled { get; set; }
+
+ ///
+ /// 每月发放日。
+ ///
+ public int GrantDay { get; set; }
+
+ ///
+ /// 券模板 ID。
+ ///
+ public List CouponTemplateIds { get; set; } = [];
+}
+
+///
+/// 免配送费权益响应。
+///
+public sealed class MemberTierFreeDeliveryBenefitResponse
+{
+ ///
+ /// 是否启用。
+ ///
+ public bool Enabled { get; set; }
+
+ ///
+ /// 每月免配送费次数。
+ ///
+ public int MonthlyFreeTimes { get; set; }
+}
+
+///
+/// 等级权益响应。
+///
+public sealed class MemberTierBenefitsResponse
+{
+ ///
+ /// 折扣权益。
+ ///
+ public MemberTierDiscountBenefitResponse Discount { get; set; } = new();
+
+ ///
+ /// 积分倍率权益。
+ ///
+ public MemberTierPointMultiplierBenefitResponse PointMultiplier { get; set; } = new();
+
+ ///
+ /// 生日特权。
+ ///
+ public MemberTierBirthdayBenefitResponse Birthday { get; set; } = new();
+
+ ///
+ /// 每月赠券。
+ ///
+ public MemberTierMonthlyCouponBenefitResponse MonthlyCoupon { get; set; } = new();
+
+ ///
+ /// 免配送费权益。
+ ///
+ public MemberTierFreeDeliveryBenefitResponse FreeDelivery { get; set; } = new();
+
+ ///
+ /// 优先配送。
+ ///
+ public bool PriorityDeliveryEnabled { get; set; }
+
+ ///
+ /// 专属客服。
+ ///
+ public bool ExclusiveServiceEnabled { get; set; }
+}
+
+///
+/// 等级详情响应。
+///
+public sealed class MemberTierDetailResponse
+{
+ ///
+ /// 等级标识。
+ ///
+ public string? TierId { get; set; }
+
+ ///
+ /// 排序序号。
+ ///
+ public int SortOrder { get; set; }
+
+ ///
+ /// 等级名称。
+ ///
+ public string Name { get; set; } = string.Empty;
+
+ ///
+ /// 图标键。
+ ///
+ public string IconKey { get; set; } = string.Empty;
+
+ ///
+ /// 主题色。
+ ///
+ public string ColorHex { get; set; } = string.Empty;
+
+ ///
+ /// 是否默认等级。
+ ///
+ public bool IsDefault { get; set; }
+
+ ///
+ /// 升降级规则。
+ ///
+ public MemberTierRuleResponse Rule { get; set; } = new();
+
+ ///
+ /// 等级权益。
+ ///
+ public MemberTierBenefitsResponse Benefits { get; set; } = new();
+
+ ///
+ /// 是否可删除。
+ ///
+ public bool CanDelete { get; set; }
+}
+
+///
+/// 保存等级请求。
+///
+public sealed class SaveMemberTierRequest
+{
+ ///
+ /// 等级标识(为空时新增)。
+ ///
+ public string? TierId { get; set; }
+
+ ///
+ /// 排序序号。
+ ///
+ public int SortOrder { get; set; }
+
+ ///
+ /// 等级名称。
+ ///
+ public string Name { get; set; } = string.Empty;
+
+ ///
+ /// 图标键。
+ ///
+ public string IconKey { get; set; } = "user";
+
+ ///
+ /// 主题色。
+ ///
+ public string ColorHex { get; set; } = "#999999";
+
+ ///
+ /// 是否默认等级。
+ ///
+ public bool IsDefault { get; set; }
+
+ ///
+ /// 升降级规则。
+ ///
+ public MemberTierRuleResponse Rule { get; set; } = new();
+
+ ///
+ /// 等级权益。
+ ///
+ public MemberTierBenefitsResponse Benefits { get; set; } = new();
+}
+
+///
+/// 删除等级请求。
+///
+public sealed class DeleteMemberTierRequest
+{
+ ///
+ /// 等级标识。
+ ///
+ public string TierId { get; set; } = string.Empty;
+}
+
+///
+/// 会员日配置响应。
+///
+public sealed class MemberDaySettingResponse
+{
+ ///
+ /// 是否启用会员日。
+ ///
+ public bool IsEnabled { get; set; }
+
+ ///
+ /// 周几(1-7,对应周一到周日)。
+ ///
+ public int Weekday { get; set; }
+
+ ///
+ /// 会员日额外折扣。
+ ///
+ public decimal ExtraDiscountRate { get; set; }
+}
+
+///
+/// 保存会员日配置请求。
+///
+public sealed class SaveMemberDaySettingRequest
+{
+ ///
+ /// 是否启用会员日。
+ ///
+ public bool IsEnabled { get; set; }
+
+ ///
+ /// 周几(1-7,对应周一到周日)。
+ ///
+ public int Weekday { get; set; }
+
+ ///
+ /// 会员日额外折扣。
+ ///
+ public decimal ExtraDiscountRate { get; set; }
+}
+
+///
+/// 优惠券选择器请求。
+///
+public sealed class MemberCouponPickerRequest
+{
+ ///
+ /// 门店 ID(可选)。
+ ///
+ public string? StoreId { get; set; }
+
+ ///
+ /// 关键词。
+ ///
+ public string? Keyword { get; set; }
+}
+
+///
+/// 优惠券选择器项响应。
+///
+public sealed class MemberCouponPickerItemResponse
+{
+ ///
+ /// 券模板标识。
+ ///
+ public string CouponTemplateId { get; set; } = string.Empty;
+
+ ///
+ /// 券名称。
+ ///
+ public string Name { get; set; } = string.Empty;
+
+ ///
+ /// 券类型。
+ ///
+ public string CouponType { get; set; } = string.Empty;
+
+ ///
+ /// 面值或折扣值。
+ ///
+ public decimal Value { get; set; }
+
+ ///
+ /// 最低消费门槛。
+ ///
+ public decimal? MinimumSpend { get; set; }
+
+ ///
+ /// 展示文案。
+ ///
+ public string DisplayText { get; set; } = string.Empty;
+}
diff --git a/src/Api/TakeoutSaaS.TenantApi/Controllers/MemberController.cs b/src/Api/TakeoutSaaS.TenantApi/Controllers/MemberController.cs
new file mode 100644
index 0000000..23184ed
--- /dev/null
+++ b/src/Api/TakeoutSaaS.TenantApi/Controllers/MemberController.cs
@@ -0,0 +1,251 @@
+using System.Globalization;
+using MediatR;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+using TakeoutSaaS.Application.App.Members.Commands;
+using TakeoutSaaS.Application.App.Members.Dto;
+using TakeoutSaaS.Application.App.Members.Queries;
+using TakeoutSaaS.Application.App.Stores.Services;
+using TakeoutSaaS.Infrastructure.App.Persistence;
+using TakeoutSaaS.Module.Authorization.Attributes;
+using TakeoutSaaS.Shared.Abstractions.Constants;
+using TakeoutSaaS.Shared.Abstractions.Exceptions;
+using TakeoutSaaS.Shared.Abstractions.Results;
+using TakeoutSaaS.Shared.Web.Api;
+using TakeoutSaaS.TenantApi.Contracts.Member;
+
+namespace TakeoutSaaS.TenantApi.Controllers;
+
+///
+/// 会员管理列表与详情。
+///
+[ApiVersion("1.0")]
+[Authorize]
+[Route("api/tenant/v{version:apiVersion}/member/list")]
+public sealed class MemberController(
+ IMediator mediator,
+ TakeoutAppDbContext dbContext,
+ StoreContextService storeContextService)
+ : BaseApiController
+{
+ private const string ViewPermission = "tenant:member:view";
+ private const string ManagePermission = "tenant:member:manage";
+
+ ///
+ /// 获取会员列表。
+ ///
+ [HttpGet("list")]
+ [PermissionAuthorize(ViewPermission, ManagePermission)]
+ [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)]
+ public async Task> List(
+ [FromQuery] MemberListRequest request,
+ CancellationToken cancellationToken)
+ {
+ var visibleStoreIds = await ResolveVisibleStoreIdsAsync(request.StoreId, cancellationToken);
+
+ var result = await mediator.Send(new SearchMemberListQuery
+ {
+ VisibleStoreIds = visibleStoreIds,
+ Keyword = request.Keyword,
+ TierId = StoreApiHelpers.ParseSnowflakeOrNull(request.TierId),
+ Page = Math.Max(1, request.Page),
+ PageSize = Math.Clamp(request.PageSize, 1, 200)
+ }, cancellationToken);
+
+ return ApiResponse.Ok(new MemberListResultResponse
+ {
+ Items = result.Items.Select(MapListItem).ToList(),
+ Total = result.TotalCount,
+ Page = result.Page,
+ PageSize = result.PageSize
+ });
+ }
+
+ ///
+ /// 获取会员列表统计。
+ ///
+ [HttpGet("stats")]
+ [PermissionAuthorize(ViewPermission, ManagePermission)]
+ [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)]
+ public async Task> Stats(
+ [FromQuery] MemberListFilterRequest request,
+ CancellationToken cancellationToken)
+ {
+ var visibleStoreIds = await ResolveVisibleStoreIdsAsync(request.StoreId, cancellationToken);
+
+ var result = await mediator.Send(new GetMemberListStatsQuery
+ {
+ VisibleStoreIds = visibleStoreIds,
+ Keyword = request.Keyword,
+ TierId = StoreApiHelpers.ParseSnowflakeOrNull(request.TierId)
+ }, cancellationToken);
+
+ return ApiResponse.Ok(new MemberListStatsResponse
+ {
+ TotalMembers = result.TotalMembers,
+ MonthlyNewMembers = result.MonthlyNewMembers,
+ ActiveMembers = result.ActiveMembers,
+ DormantMembers = result.DormantMembers
+ });
+ }
+
+ ///
+ /// 获取会员详情。
+ ///
+ [HttpGet("detail")]
+ [PermissionAuthorize(ViewPermission, ManagePermission)]
+ [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)]
+ public async Task> Detail(
+ [FromQuery] MemberDetailRequest request,
+ CancellationToken cancellationToken)
+ {
+ var visibleStoreIds = await ResolveVisibleStoreIdsAsync(request.StoreId, cancellationToken);
+
+ var result = await mediator.Send(new GetMemberDetailQuery
+ {
+ VisibleStoreIds = visibleStoreIds,
+ MemberId = StoreApiHelpers.ParseRequiredSnowflake(request.MemberId, nameof(request.MemberId))
+ }, cancellationToken);
+
+ if (result is null)
+ {
+ return ApiResponse.Error(ErrorCodes.NotFound, "会员不存在");
+ }
+
+ return ApiResponse.Ok(MapDetail(result));
+ }
+
+ ///
+ /// 保存会员标签。
+ ///
+ [HttpPost("tags")]
+ [PermissionAuthorize(ManagePermission)]
+ [ProducesResponseType(typeof(ApiResponse