fix: 门店资质日期字段改为date

This commit is contained in:
2026-01-20 13:09:06 +08:00
parent de54b64efc
commit 3385674490
17 changed files with 14948 additions and 35 deletions

View File

@@ -130,19 +130,19 @@ public sealed class GetStoreAuditDetailQueryHandler(
} }
// 2. (空行后) 映射资质 DTO // 2. (空行后) 映射资质 DTO
var now = DateTime.UtcNow.Date; var today = DateOnly.FromDateTime(DateTime.UtcNow);
while (await reader.ReadAsync(cancellationToken)) while (await reader.ReadAsync(cancellationToken))
{ {
DateTime? expiresAt = reader.IsDBNull(reader.GetOrdinal("ExpiresAt")) DateOnly? expiresAt = reader.IsDBNull(reader.GetOrdinal("ExpiresAt"))
? null ? null
: reader.GetDateTime(reader.GetOrdinal("ExpiresAt")); : DateOnly.FromDateTime(reader.GetDateTime(reader.GetOrdinal("ExpiresAt")));
int? daysUntilExpiry = expiresAt.HasValue int? daysUntilExpiry = expiresAt.HasValue
? (int)Math.Ceiling((expiresAt.Value.Date - now).TotalDays) ? expiresAt.Value.DayNumber - today.DayNumber
: null; : null;
var isExpired = expiresAt.HasValue && expiresAt.Value < DateTime.UtcNow; var isExpired = expiresAt.HasValue && expiresAt.Value < today;
var isExpiringSoon = expiresAt.HasValue var isExpiringSoon = expiresAt.HasValue
&& expiresAt.Value >= DateTime.UtcNow && expiresAt.Value >= today
&& expiresAt.Value <= DateTime.UtcNow.AddDays(30); && expiresAt.Value <= today.AddDays(30);
// 2.1 (空行后) 写入列表 // 2.1 (空行后) 写入列表
items.Add(new StoreQualificationDto items.Add(new StoreQualificationDto
@@ -156,7 +156,7 @@ public sealed class GetStoreAuditDetailQueryHandler(
: reader.GetString(reader.GetOrdinal("DocumentNumber")), : reader.GetString(reader.GetOrdinal("DocumentNumber")),
IssuedAt = reader.IsDBNull(reader.GetOrdinal("IssuedAt")) IssuedAt = reader.IsDBNull(reader.GetOrdinal("IssuedAt"))
? null ? null
: reader.GetDateTime(reader.GetOrdinal("IssuedAt")), : DateOnly.FromDateTime(reader.GetDateTime(reader.GetOrdinal("IssuedAt"))),
ExpiresAt = expiresAt, ExpiresAt = expiresAt,
IsExpired = isExpired, IsExpired = isExpired,
IsExpiringSoon = isExpiringSoon, IsExpiringSoon = isExpiringSoon,

View File

@@ -32,12 +32,12 @@ public sealed record CreateStoreQualificationCommand : IRequest<StoreQualificati
/// <summary> /// <summary>
/// 签发日期。 /// 签发日期。
/// </summary> /// </summary>
public DateTime? IssuedAt { get; init; } public DateOnly? IssuedAt { get; init; }
/// <summary> /// <summary>
/// 到期日期。 /// 到期日期。
/// </summary> /// </summary>
public DateTime? ExpiresAt { get; init; } public DateOnly? ExpiresAt { get; init; }
/// <summary> /// <summary>
/// 排序值。 /// 排序值。

View File

@@ -31,12 +31,12 @@ public sealed record UpdateStoreQualificationCommand : IRequest<StoreQualificati
/// <summary> /// <summary>
/// 签发日期。 /// 签发日期。
/// </summary> /// </summary>
public DateTime? IssuedAt { get; init; } public DateOnly? IssuedAt { get; init; }
/// <summary> /// <summary>
/// 到期日期。 /// 到期日期。
/// </summary> /// </summary>
public DateTime? ExpiresAt { get; init; } public DateOnly? ExpiresAt { get; init; }
/// <summary> /// <summary>
/// 排序值。 /// 排序值。

View File

@@ -50,7 +50,7 @@ public sealed record StoreQualificationAlertDto
/// <summary> /// <summary>
/// 过期时间。 /// 过期时间。
/// </summary> /// </summary>
public DateTime? ExpiresAt { get; init; } public DateOnly? ExpiresAt { get; init; }
/// <summary> /// <summary>
/// 距离过期天数。 /// 距离过期天数。

View File

@@ -39,12 +39,12 @@ public sealed class StoreQualificationDto
/// <summary> /// <summary>
/// 签发日期。 /// 签发日期。
/// </summary> /// </summary>
public DateTime? IssuedAt { get; init; } public DateOnly? IssuedAt { get; init; }
/// <summary> /// <summary>
/// 到期日期。 /// 到期日期。
/// </summary> /// </summary>
public DateTime? ExpiresAt { get; init; } public DateOnly? ExpiresAt { get; init; }
/// <summary> /// <summary>
/// 是否已过期。 /// 是否已过期。

View File

@@ -30,7 +30,7 @@ public sealed class ListExpiringStoreQualificationsQueryHandler(
daysThreshold = 365; daysThreshold = 365;
} }
var offset = (page - 1) * pageSize; var offset = (page - 1) * pageSize;
var now = DateTime.UtcNow; var now = DateOnly.FromDateTime(DateTime.UtcNow);
var expiringBefore = now.AddDays(daysThreshold); var expiringBefore = now.AddDays(daysThreshold);
// 2. (空行后) 执行查询 // 2. (空行后) 执行查询
@@ -92,12 +92,12 @@ public sealed class ListExpiringStoreQualificationsQueryHandler(
List<StoreQualificationAlertDto> items = []; List<StoreQualificationAlertDto> items = [];
while (await reader.ReadAsync(token)) while (await reader.ReadAsync(token))
{ {
DateTime? expiresAt = reader.IsDBNull(expiresAtOrdinal) DateOnly? expiresAt = reader.IsDBNull(expiresAtOrdinal)
? null ? null
: reader.GetDateTime(expiresAtOrdinal); : DateOnly.FromDateTime(reader.GetDateTime(expiresAtOrdinal));
var isExpired = expiresAt.HasValue && expiresAt.Value < now; var isExpired = expiresAt.HasValue && expiresAt.Value < now;
int? daysUntilExpiry = expiresAt.HasValue int? daysUntilExpiry = expiresAt.HasValue
? (int)Math.Ceiling((expiresAt.Value.Date - now.Date).TotalDays) ? expiresAt.Value.DayNumber - now.DayNumber
: null; : null;
items.Add(new StoreQualificationAlertDto items.Add(new StoreQualificationAlertDto
@@ -146,8 +146,8 @@ public sealed class ListExpiringStoreQualificationsQueryHandler(
private static async Task<StoreQualificationAlertSummaryDto> ExecuteSummaryAsync( private static async Task<StoreQualificationAlertSummaryDto> ExecuteSummaryAsync(
IDbConnection connection, IDbConnection connection,
DateTime now, DateOnly now,
DateTime expiringBefore, DateOnly expiringBefore,
long? tenantId, long? tenantId,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {

View File

@@ -78,8 +78,9 @@ public static class StoreMapping
/// <returns>DTO。</returns> /// <returns>DTO。</returns>
public static StoreQualificationDto ToDto(StoreQualification qualification) public static StoreQualificationDto ToDto(StoreQualification qualification)
{ {
var today = DateOnly.FromDateTime(DateTime.UtcNow);
int? daysUntilExpiry = qualification.ExpiresAt.HasValue int? daysUntilExpiry = qualification.ExpiresAt.HasValue
? (int)Math.Ceiling((qualification.ExpiresAt.Value.Date - DateTime.UtcNow.Date).TotalDays) ? qualification.ExpiresAt.Value.DayNumber - today.DayNumber
: null; : null;
return new StoreQualificationDto return new StoreQualificationDto

View File

@@ -21,7 +21,7 @@ public sealed class CreateStoreQualificationCommandValidator : AbstractValidator
RuleFor(x => x.DocumentNumber).MaximumLength(100); RuleFor(x => x.DocumentNumber).MaximumLength(100);
RuleFor(x => x.ExpiresAt) RuleFor(x => x.ExpiresAt)
.Must(date => date.HasValue && date.Value.Date > DateTime.UtcNow.Date) .Must(date => date.HasValue && date.Value > DateOnly.FromDateTime(DateTime.UtcNow))
.When(x => IsLicenseType(x.QualificationType)) .When(x => IsLicenseType(x.QualificationType))
.WithMessage("证照有效期必须晚于今天"); .WithMessage("证照有效期必须晚于今天");
@@ -32,7 +32,7 @@ public sealed class CreateStoreQualificationCommandValidator : AbstractValidator
.WithMessage("证照编号不能为空"); .WithMessage("证照编号不能为空");
RuleFor(x => x.ExpiresAt) RuleFor(x => x.ExpiresAt)
.Must(date => !date.HasValue || date.Value.Date > DateTime.UtcNow.Date) .Must(date => !date.HasValue || date.Value > DateOnly.FromDateTime(DateTime.UtcNow))
.When(x => !IsLicenseType(x.QualificationType)) .When(x => !IsLicenseType(x.QualificationType))
.WithMessage("证照有效期必须晚于今天"); .WithMessage("证照有效期必须晚于今天");
} }

View File

@@ -19,7 +19,7 @@ public sealed class UpdateStoreQualificationCommandValidator : AbstractValidator
RuleFor(x => x.DocumentNumber).MaximumLength(100).When(x => !string.IsNullOrWhiteSpace(x.DocumentNumber)); RuleFor(x => x.DocumentNumber).MaximumLength(100).When(x => !string.IsNullOrWhiteSpace(x.DocumentNumber));
RuleFor(x => x.SortOrder).GreaterThanOrEqualTo(0).When(x => x.SortOrder.HasValue); RuleFor(x => x.SortOrder).GreaterThanOrEqualTo(0).When(x => x.SortOrder.HasValue);
RuleFor(x => x.ExpiresAt) RuleFor(x => x.ExpiresAt)
.Must(date => !date.HasValue || date.Value.Date > DateTime.UtcNow.Date) .Must(date => !date.HasValue || date.Value > DateOnly.FromDateTime(DateTime.UtcNow))
.When(x => x.ExpiresAt.HasValue) .When(x => x.ExpiresAt.HasValue)
.WithMessage("证照有效期必须晚于今天"); .WithMessage("证照有效期必须晚于今天");
} }

View File

@@ -31,24 +31,25 @@ public sealed class StoreQualification : MultiTenantEntityBase
/// <summary> /// <summary>
/// 签发日期。 /// 签发日期。
/// </summary> /// </summary>
public DateTime? IssuedAt { get; set; } public DateOnly? IssuedAt { get; set; }
/// <summary> /// <summary>
/// 到期日期。 /// 到期日期。
/// </summary> /// </summary>
public DateTime? ExpiresAt { get; set; } public DateOnly? ExpiresAt { get; set; }
/// <summary> /// <summary>
/// 是否已过期。 /// 是否已过期。
/// </summary> /// </summary>
public bool IsExpired => ExpiresAt.HasValue && ExpiresAt.Value < DateTime.UtcNow; public bool IsExpired => ExpiresAt.HasValue
&& ExpiresAt.Value < DateOnly.FromDateTime(DateTime.UtcNow);
/// <summary> /// <summary>
/// 是否即将过期30天内 /// 是否即将过期30天内
/// </summary> /// </summary>
public bool IsExpiringSoon => ExpiresAt.HasValue public bool IsExpiringSoon => ExpiresAt.HasValue
&& ExpiresAt.Value >= DateTime.UtcNow && ExpiresAt.Value >= DateOnly.FromDateTime(DateTime.UtcNow)
&& ExpiresAt.Value <= DateTime.UtcNow.AddDays(30); && ExpiresAt.Value <= DateOnly.FromDateTime(DateTime.UtcNow).AddDays(30);
/// <summary> /// <summary>
/// 排序值。 /// 排序值。

View File

@@ -625,6 +625,8 @@ public sealed class TakeoutAppDbContext(
builder.Property(x => x.QualificationType).HasConversion<int>(); builder.Property(x => x.QualificationType).HasConversion<int>();
builder.Property(x => x.FileUrl).HasMaxLength(500).IsRequired(); builder.Property(x => x.FileUrl).HasMaxLength(500).IsRequired();
builder.Property(x => x.DocumentNumber).HasMaxLength(100); builder.Property(x => x.DocumentNumber).HasMaxLength(100);
builder.Property(x => x.IssuedAt).HasColumnType("date");
builder.Property(x => x.ExpiresAt).HasColumnType("date");
builder.Property(x => x.SortOrder).HasDefaultValue(100); builder.Property(x => x.SortOrder).HasDefaultValue(100);
builder.HasIndex(x => new { x.TenantId, x.StoreId }); builder.HasIndex(x => new { x.TenantId, x.StoreId });
builder.HasIndex(x => x.ExpiresAt) builder.HasIndex(x => x.ExpiresAt)

View File

@@ -105,11 +105,12 @@ public sealed class StoreSchedulerService(
public async Task<int> CheckQualificationExpiryAsync(DateTime now, CancellationToken cancellationToken) public async Task<int> CheckQualificationExpiryAsync(DateTime now, CancellationToken cancellationToken)
{ {
// 1. 查询过期门店 // 1. 查询过期门店
var today = DateOnly.FromDateTime(now);
var expiredStoreIds = await context.StoreQualifications var expiredStoreIds = await context.StoreQualifications
.AsNoTracking() .AsNoTracking()
.Where(qualification => qualification.DeletedAt == null .Where(qualification => qualification.DeletedAt == null
&& qualification.ExpiresAt.HasValue && qualification.ExpiresAt.HasValue
&& qualification.ExpiresAt.Value < now) && qualification.ExpiresAt.Value < today)
.Select(qualification => qualification.StoreId) .Select(qualification => qualification.StoreId)
.Distinct() .Distinct()
.ToListAsync(cancellationToken); .ToListAsync(cancellationToken);

View File

@@ -0,0 +1,63 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace TakeoutSaaS.Infrastructure.Migrations
{
/// <inheritdoc />
public partial class ChangeStoreQualificationDateColumns : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<DateOnly>(
name: "IssuedAt",
table: "store_qualifications",
type: "date",
nullable: true,
comment: "签发日期。",
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone",
oldNullable: true,
oldComment: "签发日期。");
migrationBuilder.AlterColumn<DateOnly>(
name: "ExpiresAt",
table: "store_qualifications",
type: "date",
nullable: true,
comment: "到期日期。",
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone",
oldNullable: true,
oldComment: "到期日期。");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<DateTime>(
name: "IssuedAt",
table: "store_qualifications",
type: "timestamp with time zone",
nullable: true,
comment: "签发日期。",
oldClrType: typeof(DateOnly),
oldType: "date",
oldNullable: true,
oldComment: "签发日期。");
migrationBuilder.AlterColumn<DateTime>(
name: "ExpiresAt",
table: "store_qualifications",
type: "timestamp with time zone",
nullable: true,
comment: "到期日期。",
oldClrType: typeof(DateOnly),
oldType: "date",
oldNullable: true,
oldComment: "到期日期。");
}
}
}

View File

@@ -0,0 +1,63 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace TakeoutSaaS.Infrastructure.Migrations
{
/// <inheritdoc />
public partial class UpdateStoreQualificationDateFields : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<DateOnly>(
name: "IssuedAt",
table: "store_qualifications",
type: "date",
nullable: true,
comment: "签发日期。",
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone",
oldNullable: true,
oldComment: "签发日期。");
migrationBuilder.AlterColumn<DateOnly>(
name: "ExpiresAt",
table: "store_qualifications",
type: "date",
nullable: true,
comment: "到期日期。",
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone",
oldNullable: true,
oldComment: "到期日期。");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<DateTime>(
name: "IssuedAt",
table: "store_qualifications",
type: "timestamp with time zone",
nullable: true,
comment: "签发日期。",
oldClrType: typeof(DateOnly),
oldType: "date",
oldNullable: true,
oldComment: "签发日期。");
migrationBuilder.AlterColumn<DateTime>(
name: "ExpiresAt",
table: "store_qualifications",
type: "timestamp with time zone",
nullable: true,
comment: "到期日期。",
oldClrType: typeof(DateOnly),
oldType: "date",
oldNullable: true,
oldComment: "到期日期。");
}
}
}

View File

@@ -5731,8 +5731,8 @@ namespace TakeoutSaaS.Infrastructure.Migrations
.HasColumnType("character varying(100)") .HasColumnType("character varying(100)")
.HasComment("证照编号。"); .HasComment("证照编号。");
b.Property<DateTime?>("ExpiresAt") b.Property<DateOnly?>("ExpiresAt")
.HasColumnType("timestamp with time zone") .HasColumnType("date")
.HasComment("到期日期。"); .HasComment("到期日期。");
b.Property<string>("FileUrl") b.Property<string>("FileUrl")
@@ -5741,8 +5741,8 @@ namespace TakeoutSaaS.Infrastructure.Migrations
.HasColumnType("character varying(500)") .HasColumnType("character varying(500)")
.HasComment("证照文件 URL。"); .HasComment("证照文件 URL。");
b.Property<DateTime?>("IssuedAt") b.Property<DateOnly?>("IssuedAt")
.HasColumnType("timestamp with time zone") .HasColumnType("date")
.HasComment("签发日期。"); .HasComment("签发日期。");
b.Property<int>("QualificationType") b.Property<int>("QualificationType")