完成门店管理后端接口与任务
This commit is contained in:
@@ -53,6 +53,66 @@ public sealed class Store : MultiTenantEntityBase
|
||||
/// </summary>
|
||||
public string? BusinessLicenseImageUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 门头招牌图 URL。
|
||||
/// </summary>
|
||||
public string? SignboardImageUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 主体类型。
|
||||
/// </summary>
|
||||
public StoreOwnershipType OwnershipType { get; set; } = StoreOwnershipType.SameEntity;
|
||||
|
||||
/// <summary>
|
||||
/// 审核状态。
|
||||
/// </summary>
|
||||
public StoreAuditStatus AuditStatus { get; set; } = StoreAuditStatus.Draft;
|
||||
|
||||
/// <summary>
|
||||
/// 经营状态。
|
||||
/// </summary>
|
||||
public StoreBusinessStatus BusinessStatus { get; set; } = StoreBusinessStatus.Resting;
|
||||
|
||||
/// <summary>
|
||||
/// 歇业原因。
|
||||
/// </summary>
|
||||
public StoreClosureReason? ClosureReason { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 歇业原因补充说明。
|
||||
/// </summary>
|
||||
public string? ClosureReasonText { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 行业类目 ID。
|
||||
/// </summary>
|
||||
public long? CategoryId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 审核驳回原因。
|
||||
/// </summary>
|
||||
public string? RejectionReason { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 提交审核时间。
|
||||
/// </summary>
|
||||
public DateTime? SubmittedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 审核通过时间。
|
||||
/// </summary>
|
||||
public DateTime? ActivatedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 强制关闭时间。
|
||||
/// </summary>
|
||||
public DateTime? ForceClosedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 强制关闭原因。
|
||||
/// </summary>
|
||||
public string? ForceCloseReason { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 门店当前运营状态。
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
using TakeoutSaaS.Domain.Stores.Enums;
|
||||
using TakeoutSaaS.Shared.Abstractions.Entities;
|
||||
|
||||
namespace TakeoutSaaS.Domain.Stores.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// 门店审核记录。
|
||||
/// </summary>
|
||||
public sealed class StoreAuditRecord : MultiTenantEntityBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 门店标识。
|
||||
/// </summary>
|
||||
public long StoreId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 操作类型。
|
||||
/// </summary>
|
||||
public StoreAuditAction Action { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 操作前状态。
|
||||
/// </summary>
|
||||
public StoreAuditStatus? PreviousStatus { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 操作后状态。
|
||||
/// </summary>
|
||||
public StoreAuditStatus NewStatus { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 操作人 ID。
|
||||
/// </summary>
|
||||
public long? OperatorId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 操作人名称。
|
||||
/// </summary>
|
||||
public string OperatorName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 驳回理由 ID。
|
||||
/// </summary>
|
||||
public long? RejectionReasonId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 驳回理由文本。
|
||||
/// </summary>
|
||||
public string? RejectionReason { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 备注。
|
||||
/// </summary>
|
||||
public string? Remarks { get; set; }
|
||||
}
|
||||
40
src/Domain/TakeoutSaaS.Domain/Stores/Entities/StoreFee.cs
Normal file
40
src/Domain/TakeoutSaaS.Domain/Stores/Entities/StoreFee.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using TakeoutSaaS.Domain.Stores.Enums;
|
||||
using TakeoutSaaS.Shared.Abstractions.Entities;
|
||||
|
||||
namespace TakeoutSaaS.Domain.Stores.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// 门店费用配置。
|
||||
/// </summary>
|
||||
public sealed class StoreFee : MultiTenantEntityBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 门店标识。
|
||||
/// </summary>
|
||||
public long StoreId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 起送费(元)。
|
||||
/// </summary>
|
||||
public decimal MinimumOrderAmount { get; set; } = 0m;
|
||||
|
||||
/// <summary>
|
||||
/// 基础配送费(元)。
|
||||
/// </summary>
|
||||
public decimal BaseDeliveryFee { get; set; } = 0m;
|
||||
|
||||
/// <summary>
|
||||
/// 打包费模式。
|
||||
/// </summary>
|
||||
public PackagingFeeMode PackagingFeeMode { get; set; } = PackagingFeeMode.Fixed;
|
||||
|
||||
/// <summary>
|
||||
/// 固定打包费(总计模式有效)。
|
||||
/// </summary>
|
||||
public decimal FixedPackagingFee { get; set; } = 0m;
|
||||
|
||||
/// <summary>
|
||||
/// 免配送费门槛。
|
||||
/// </summary>
|
||||
public decimal? FreeDeliveryThreshold { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using TakeoutSaaS.Domain.Stores.Enums;
|
||||
using TakeoutSaaS.Shared.Abstractions.Entities;
|
||||
|
||||
namespace TakeoutSaaS.Domain.Stores.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// 门店资质证照。
|
||||
/// </summary>
|
||||
public sealed class StoreQualification : MultiTenantEntityBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 门店标识。
|
||||
/// </summary>
|
||||
public long StoreId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 资质类型。
|
||||
/// </summary>
|
||||
public StoreQualificationType QualificationType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 证照文件 URL。
|
||||
/// </summary>
|
||||
public string FileUrl { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 证照编号。
|
||||
/// </summary>
|
||||
public string? DocumentNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 签发日期。
|
||||
/// </summary>
|
||||
public DateTime? IssuedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 到期日期。
|
||||
/// </summary>
|
||||
public DateTime? ExpiresAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否已过期。
|
||||
/// </summary>
|
||||
public bool IsExpired => ExpiresAt.HasValue && ExpiresAt.Value < DateTime.UtcNow;
|
||||
|
||||
/// <summary>
|
||||
/// 是否即将过期(30天内)。
|
||||
/// </summary>
|
||||
public bool IsExpiringSoon => ExpiresAt.HasValue
|
||||
&& ExpiresAt.Value >= DateTime.UtcNow
|
||||
&& ExpiresAt.Value <= DateTime.UtcNow.AddDays(30);
|
||||
|
||||
/// <summary>
|
||||
/// 排序值。
|
||||
/// </summary>
|
||||
public int SortOrder { get; set; } = 100;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace TakeoutSaaS.Domain.Stores.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// 打包费计算模式。
|
||||
/// </summary>
|
||||
public enum PackagingFeeMode
|
||||
{
|
||||
/// <summary>
|
||||
/// 总计模式:固定单笔订单打包费。
|
||||
/// </summary>
|
||||
Fixed = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 商品计费模式:按商品累计打包费。
|
||||
/// </summary>
|
||||
PerItem = 1
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
namespace TakeoutSaaS.Domain.Stores.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// 门店审核操作类型。
|
||||
/// </summary>
|
||||
public enum StoreAuditAction
|
||||
{
|
||||
/// <summary>
|
||||
/// 提交审核。
|
||||
/// </summary>
|
||||
Submit = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 重新提交。
|
||||
/// </summary>
|
||||
Resubmit = 1,
|
||||
|
||||
/// <summary>
|
||||
/// 审核通过。
|
||||
/// </summary>
|
||||
Approve = 2,
|
||||
|
||||
/// <summary>
|
||||
/// 审核驳回。
|
||||
/// </summary>
|
||||
Reject = 3,
|
||||
|
||||
/// <summary>
|
||||
/// 强制关闭。
|
||||
/// </summary>
|
||||
ForceClose = 4,
|
||||
|
||||
/// <summary>
|
||||
/// 解除关闭。
|
||||
/// </summary>
|
||||
Reopen = 5,
|
||||
|
||||
/// <summary>
|
||||
/// 自动激活(同主体门店)。
|
||||
/// </summary>
|
||||
AutoActivate = 6
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace TakeoutSaaS.Domain.Stores.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// 门店审核状态。
|
||||
/// </summary>
|
||||
public enum StoreAuditStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// 草稿,未提交审核。
|
||||
/// </summary>
|
||||
Draft = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 审核中。
|
||||
/// </summary>
|
||||
Pending = 1,
|
||||
|
||||
/// <summary>
|
||||
/// 已激活(审核通过或自动通过)。
|
||||
/// </summary>
|
||||
Activated = 2,
|
||||
|
||||
/// <summary>
|
||||
/// 已驳回。
|
||||
/// </summary>
|
||||
Rejected = 3
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace TakeoutSaaS.Domain.Stores.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// 门店经营状态。
|
||||
/// </summary>
|
||||
public enum StoreBusinessStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// 营业中。
|
||||
/// </summary>
|
||||
Open = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 休息中(手动或自动)。
|
||||
/// </summary>
|
||||
Resting = 1,
|
||||
|
||||
/// <summary>
|
||||
/// 强制关闭(平台风控)。
|
||||
/// </summary>
|
||||
ForceClosed = 2
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
namespace TakeoutSaaS.Domain.Stores.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// 门店歇业原因(关联字典表 store_closure_reason)。
|
||||
/// </summary>
|
||||
public enum StoreClosureReason
|
||||
{
|
||||
/// <summary>
|
||||
/// 非营业时间(系统自动)。
|
||||
/// </summary>
|
||||
OutOfBusinessHours = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 设备检修。
|
||||
/// </summary>
|
||||
EquipmentMaintenance = 1,
|
||||
|
||||
/// <summary>
|
||||
/// 老板休假。
|
||||
/// </summary>
|
||||
OwnerVacation = 2,
|
||||
|
||||
/// <summary>
|
||||
/// 食材告罄。
|
||||
/// </summary>
|
||||
OutOfStock = 3,
|
||||
|
||||
/// <summary>
|
||||
/// 暂停营业。
|
||||
/// </summary>
|
||||
TemporarilyClosed = 4,
|
||||
|
||||
/// <summary>
|
||||
/// 证照过期。
|
||||
/// </summary>
|
||||
LicenseExpired = 5,
|
||||
|
||||
/// <summary>
|
||||
/// 平台封禁。
|
||||
/// </summary>
|
||||
PlatformSuspended = 6,
|
||||
|
||||
/// <summary>
|
||||
/// 其他原因。
|
||||
/// </summary>
|
||||
Other = 99
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace TakeoutSaaS.Domain.Stores.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// 门店主体类型。
|
||||
/// </summary>
|
||||
public enum StoreOwnershipType
|
||||
{
|
||||
/// <summary>
|
||||
/// 同一主体(自营)。
|
||||
/// </summary>
|
||||
SameEntity = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 不同主体(外部入驻)。
|
||||
/// </summary>
|
||||
DifferentEntity = 1
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace TakeoutSaaS.Domain.Stores.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// 门店资质类型。
|
||||
/// </summary>
|
||||
public enum StoreQualificationType
|
||||
{
|
||||
/// <summary>
|
||||
/// 营业执照。
|
||||
/// </summary>
|
||||
BusinessLicense = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 食品经营许可证。
|
||||
/// </summary>
|
||||
FoodServiceLicense = 1,
|
||||
|
||||
/// <summary>
|
||||
/// 门头实景照。
|
||||
/// </summary>
|
||||
StorefrontPhoto = 2,
|
||||
|
||||
/// <summary>
|
||||
/// 店内环境照。
|
||||
/// </summary>
|
||||
InteriorPhoto = 3
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace TakeoutSaaS.Domain.Stores.Events;
|
||||
|
||||
/// <summary>
|
||||
/// 门店审核通过事件。
|
||||
/// </summary>
|
||||
public sealed class StoreApprovedEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// 门店 ID。
|
||||
/// </summary>
|
||||
public long StoreId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 租户 ID。
|
||||
/// </summary>
|
||||
public long TenantId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 审核通过时间(UTC)。
|
||||
/// </summary>
|
||||
public DateTime ApprovedAt { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace TakeoutSaaS.Domain.Stores.Events;
|
||||
|
||||
/// <summary>
|
||||
/// 门店强制关闭事件。
|
||||
/// </summary>
|
||||
public sealed class StoreForceClosedEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// 门店 ID。
|
||||
/// </summary>
|
||||
public long StoreId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 租户 ID。
|
||||
/// </summary>
|
||||
public long TenantId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 强制关闭原因。
|
||||
/// </summary>
|
||||
public string? Reason { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 强制关闭时间(UTC)。
|
||||
/// </summary>
|
||||
public DateTime ForceClosedAt { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
namespace TakeoutSaaS.Domain.Stores.Events;
|
||||
|
||||
/// <summary>
|
||||
/// 门店审核驳回事件。
|
||||
/// </summary>
|
||||
public sealed class StoreRejectedEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// 门店 ID。
|
||||
/// </summary>
|
||||
public long StoreId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 租户 ID。
|
||||
/// </summary>
|
||||
public long TenantId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 驳回理由 ID。
|
||||
/// </summary>
|
||||
public long? RejectionReasonId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 驳回理由文本。
|
||||
/// </summary>
|
||||
public string? RejectionReason { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 驳回时间(UTC)。
|
||||
/// </summary>
|
||||
public DateTime RejectedAt { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace TakeoutSaaS.Domain.Stores.Events;
|
||||
|
||||
/// <summary>
|
||||
/// 门店提交审核事件。
|
||||
/// </summary>
|
||||
public sealed class StoreSubmittedEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// 门店 ID。
|
||||
/// </summary>
|
||||
public long StoreId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 租户 ID。
|
||||
/// </summary>
|
||||
public long TenantId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 提交时间(UTC)。
|
||||
/// </summary>
|
||||
public DateTime SubmittedAt { get; init; }
|
||||
}
|
||||
@@ -21,7 +21,26 @@ public interface IStoreRepository
|
||||
/// <summary>
|
||||
/// 按租户筛选门店列表。
|
||||
/// </summary>
|
||||
Task<IReadOnlyList<Store>> SearchAsync(long tenantId, StoreStatus? status, CancellationToken cancellationToken = default);
|
||||
Task<IReadOnlyList<Store>> SearchAsync(
|
||||
long tenantId,
|
||||
long? merchantId,
|
||||
StoreStatus? status,
|
||||
StoreAuditStatus? auditStatus,
|
||||
StoreBusinessStatus? businessStatus,
|
||||
StoreOwnershipType? ownershipType,
|
||||
string? keyword,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 判断指定坐标是否存在 100 米内门店。
|
||||
/// </summary>
|
||||
Task<bool> ExistsStoreWithinDistanceAsync(
|
||||
long merchantId,
|
||||
long tenantId,
|
||||
double longitude,
|
||||
double latitude,
|
||||
double distanceMeters,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定商户集合的门店数量。
|
||||
@@ -33,6 +52,56 @@ public interface IStoreRepository
|
||||
/// </summary>
|
||||
Task<IReadOnlyList<StoreBusinessHour>> GetBusinessHoursAsync(long storeId, long tenantId, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 获取门店费用配置。
|
||||
/// </summary>
|
||||
Task<StoreFee?> GetStoreFeeAsync(long storeId, long tenantId, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 新增门店费用配置。
|
||||
/// </summary>
|
||||
Task AddStoreFeeAsync(StoreFee storeFee, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 更新门店费用配置。
|
||||
/// </summary>
|
||||
Task UpdateStoreFeeAsync(StoreFee storeFee, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 获取门店资质列表。
|
||||
/// </summary>
|
||||
Task<IReadOnlyList<StoreQualification>> GetQualificationsAsync(long storeId, long tenantId, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 依据标识获取门店资质。
|
||||
/// </summary>
|
||||
Task<StoreQualification?> FindQualificationByIdAsync(long qualificationId, long tenantId, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 新增门店资质。
|
||||
/// </summary>
|
||||
Task AddQualificationAsync(StoreQualification qualification, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 更新门店资质。
|
||||
/// </summary>
|
||||
Task UpdateQualificationAsync(StoreQualification qualification, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 删除门店资质。
|
||||
/// </summary>
|
||||
Task DeleteQualificationAsync(long qualificationId, long tenantId, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 新增门店审核记录。
|
||||
/// </summary>
|
||||
Task AddAuditRecordAsync(StoreAuditRecord record, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 获取门店审核记录。
|
||||
/// </summary>
|
||||
Task<IReadOnlyList<StoreAuditRecord>> GetAuditRecordsAsync(long storeId, long tenantId, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 依据标识获取营业时段。
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user