feat: 身份操作日志改造为Outbox并修正日志库连接
This commit is contained in:
@@ -13,6 +13,7 @@ using TakeoutSaaS.Application.Sms.Extensions;
|
||||
using TakeoutSaaS.Application.Storage.Extensions;
|
||||
using TakeoutSaaS.Infrastructure.App.Extensions;
|
||||
using TakeoutSaaS.Infrastructure.Identity.Extensions;
|
||||
using TakeoutSaaS.Infrastructure.Logs.Extensions;
|
||||
using TakeoutSaaS.Module.Authorization.Extensions;
|
||||
using TakeoutSaaS.Module.Dictionary.Extensions;
|
||||
using TakeoutSaaS.Module.Messaging.Extensions;
|
||||
@@ -76,6 +77,7 @@ builder.Services.AddSmsModule(builder.Configuration);
|
||||
builder.Services.AddSmsApplication(builder.Configuration);
|
||||
builder.Services.AddMessagingModule(builder.Configuration);
|
||||
builder.Services.AddMessagingApplication();
|
||||
builder.Services.AddOperationLogOutbox(builder.Configuration);
|
||||
builder.Services.AddSchedulerModule(builder.Configuration);
|
||||
builder.Services.AddHealthChecks();
|
||||
builder.Services.AddRateLimiter(options =>
|
||||
|
||||
@@ -32,9 +32,9 @@
|
||||
"MaxRetryDelaySeconds": 5
|
||||
},
|
||||
"LogsDatabase": {
|
||||
"Write": "Host=120.53.222.17;Port=5432;Database=takeout_logs_db;Username=pg_roles;Password=Logs112233;Pooling=true;Minimum Pool Size=5;Maximum Pool Size=50",
|
||||
"Write": "Host=120.53.222.17;Port=5432;Database=takeout_logs_db;Username=logs_user;Password=Logs112233;Pooling=true;Minimum Pool Size=5;Maximum Pool Size=50",
|
||||
"Reads": [
|
||||
"Host=120.53.222.17;Port=5432;Database=takeout_logs_db;Username=pg_roles;Password=Logs112233;Pooling=true;Minimum Pool Size=5;Maximum Pool Size=50"
|
||||
"Host=120.53.222.17;Port=5432;Database=takeout_logs_db;Username=logs_user;Password=Logs112233;Pooling=true;Minimum Pool Size=5;Maximum Pool Size=50"
|
||||
],
|
||||
"CommandTimeoutSeconds": 30,
|
||||
"MaxRetryCount": 3,
|
||||
|
||||
@@ -32,9 +32,9 @@
|
||||
"MaxRetryDelaySeconds": 5
|
||||
},
|
||||
"LogsDatabase": {
|
||||
"Write": "Host=120.53.222.17;Port=5432;Database=takeout_logs_db;Username=pg_roles;Password=Logs112233;Pooling=true;Minimum Pool Size=5;Maximum Pool Size=50",
|
||||
"Write": "Host=120.53.222.17;Port=5432;Database=takeout_logs_db;Username=logs_user;Password=Logs112233;Pooling=true;Minimum Pool Size=5;Maximum Pool Size=50",
|
||||
"Reads": [
|
||||
"Host=120.53.222.17;Port=5432;Database=takeout_logs_db;Username=pg_roles;Password=Logs112233;Pooling=true;Minimum Pool Size=5;Maximum Pool Size=50"
|
||||
"Host=120.53.222.17;Port=5432;Database=takeout_logs_db;Username=logs_user;Password=Logs112233;Pooling=true;Minimum Pool Size=5;Maximum Pool Size=50"
|
||||
],
|
||||
"CommandTimeoutSeconds": 30,
|
||||
"MaxRetryCount": 3,
|
||||
|
||||
@@ -29,9 +29,9 @@
|
||||
"MaxRetryDelaySeconds": 5
|
||||
},
|
||||
"LogsDatabase": {
|
||||
"Write": "Host=120.53.222.17;Port=5432;Database=takeout_logs_db;Username=pg_roles;Password=Logs112233;Pooling=true;Minimum Pool Size=5;Maximum Pool Size=50",
|
||||
"Write": "Host=120.53.222.17;Port=5432;Database=takeout_logs_db;Username=logs_user;Password=Logs112233;Pooling=true;Minimum Pool Size=5;Maximum Pool Size=50",
|
||||
"Reads": [
|
||||
"Host=120.53.222.17;Port=5432;Database=takeout_logs_db;Username=pg_roles;Password=Logs112233;Pooling=true;Minimum Pool Size=5;Maximum Pool Size=50"
|
||||
"Host=120.53.222.17;Port=5432;Database=takeout_logs_db;Username=logs_user;Password=Logs112233;Pooling=true;Minimum Pool Size=5;Maximum Pool Size=50"
|
||||
],
|
||||
"CommandTimeoutSeconds": 30,
|
||||
"MaxRetryCount": 3,
|
||||
|
||||
@@ -29,9 +29,9 @@
|
||||
"MaxRetryDelaySeconds": 5
|
||||
},
|
||||
"LogsDatabase": {
|
||||
"Write": "Host=120.53.222.17;Port=5432;Database=takeout_logs_db;Username=pg_roles;Password=Logs112233;Pooling=true;Minimum Pool Size=5;Maximum Pool Size=50",
|
||||
"Write": "Host=120.53.222.17;Port=5432;Database=takeout_logs_db;Username=logs_user;Password=Logs112233;Pooling=true;Minimum Pool Size=5;Maximum Pool Size=50",
|
||||
"Reads": [
|
||||
"Host=120.53.222.17;Port=5432;Database=takeout_logs_db;Username=pg_roles;Password=Logs112233;Pooling=true;Minimum Pool Size=5;Maximum Pool Size=50"
|
||||
"Host=120.53.222.17;Port=5432;Database=takeout_logs_db;Username=logs_user;Password=Logs112233;Pooling=true;Minimum Pool Size=5;Maximum Pool Size=50"
|
||||
],
|
||||
"CommandTimeoutSeconds": 30,
|
||||
"MaxRetryCount": 3,
|
||||
|
||||
@@ -29,9 +29,9 @@
|
||||
"MaxRetryDelaySeconds": 5
|
||||
},
|
||||
"LogsDatabase": {
|
||||
"Write": "Host=120.53.222.17;Port=5432;Database=takeout_logs_db;Username=pg_roles;Password=Logs112233;Pooling=true;Minimum Pool Size=5;Maximum Pool Size=50",
|
||||
"Write": "Host=120.53.222.17;Port=5432;Database=takeout_logs_db;Username=logs_user;Password=Logs112233;Pooling=true;Minimum Pool Size=5;Maximum Pool Size=50",
|
||||
"Reads": [
|
||||
"Host=120.53.222.17;Port=5432;Database=takeout_logs_db;Username=pg_roles;Password=Logs112233;Pooling=true;Minimum Pool Size=5;Maximum Pool Size=50"
|
||||
"Host=120.53.222.17;Port=5432;Database=takeout_logs_db;Username=logs_user;Password=Logs112233;Pooling=true;Minimum Pool Size=5;Maximum Pool Size=50"
|
||||
],
|
||||
"CommandTimeoutSeconds": 30,
|
||||
"MaxRetryCount": 3,
|
||||
|
||||
@@ -29,9 +29,9 @@
|
||||
"MaxRetryDelaySeconds": 5
|
||||
},
|
||||
"LogsDatabase": {
|
||||
"Write": "Host=120.53.222.17;Port=5432;Database=takeout_logs_db;Username=pg_roles;Password=Logs112233;Pooling=true;Minimum Pool Size=5;Maximum Pool Size=50",
|
||||
"Write": "Host=120.53.222.17;Port=5432;Database=takeout_logs_db;Username=logs_user;Password=Logs112233;Pooling=true;Minimum Pool Size=5;Maximum Pool Size=50",
|
||||
"Reads": [
|
||||
"Host=120.53.222.17;Port=5432;Database=takeout_logs_db;Username=pg_roles;Password=Logs112233;Pooling=true;Minimum Pool Size=5;Maximum Pool Size=50"
|
||||
"Host=120.53.222.17;Port=5432;Database=takeout_logs_db;Username=logs_user;Password=Logs112233;Pooling=true;Minimum Pool Size=5;Maximum Pool Size=50"
|
||||
],
|
||||
"CommandTimeoutSeconds": 30,
|
||||
"MaxRetryCount": 3,
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
using TakeoutSaaS.Application.Identity.Events;
|
||||
|
||||
namespace TakeoutSaaS.Application.Identity.Abstractions;
|
||||
|
||||
/// <summary>
|
||||
/// 身份模块操作日志发布器。
|
||||
/// </summary>
|
||||
public interface IIdentityOperationLogPublisher
|
||||
{
|
||||
/// <summary>
|
||||
/// 发布身份模块操作日志消息。
|
||||
/// </summary>
|
||||
/// <param name="message">操作日志消息。</param>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>异步任务。</returns>
|
||||
Task PublishAsync(IdentityUserOperationLogMessage message, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
namespace TakeoutSaaS.Application.Identity.Events;
|
||||
|
||||
/// <summary>
|
||||
/// 身份用户操作日志消息。
|
||||
/// </summary>
|
||||
public sealed record IdentityUserOperationLogMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// 操作类型。
|
||||
/// </summary>
|
||||
public string OperationType { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 目标类型。
|
||||
/// </summary>
|
||||
public string TargetType { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 目标 ID 列表(JSON)。
|
||||
/// </summary>
|
||||
public string? TargetIds { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 操作人 ID。
|
||||
/// </summary>
|
||||
public string? OperatorId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 操作人名称。
|
||||
/// </summary>
|
||||
public string? OperatorName { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 操作参数(JSON)。
|
||||
/// </summary>
|
||||
public string? Parameters { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 操作结果(JSON)。
|
||||
/// </summary>
|
||||
public string? Result { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否成功。
|
||||
/// </summary>
|
||||
public bool Success { get; init; }
|
||||
}
|
||||
@@ -3,12 +3,11 @@ using System.Text.Json;
|
||||
using TakeoutSaaS.Application.Identity.Abstractions;
|
||||
using TakeoutSaaS.Application.Identity.Commands;
|
||||
using TakeoutSaaS.Application.Identity.Contracts;
|
||||
using TakeoutSaaS.Application.Identity.Events;
|
||||
using TakeoutSaaS.Application.Identity.Models;
|
||||
using TakeoutSaaS.Domain.Identity.Entities;
|
||||
using TakeoutSaaS.Domain.Identity.Enums;
|
||||
using TakeoutSaaS.Domain.Identity.Repositories;
|
||||
using TakeoutSaaS.Domain.Tenants.Entities;
|
||||
using TakeoutSaaS.Domain.Tenants.Repositories;
|
||||
using TakeoutSaaS.Shared.Abstractions.Constants;
|
||||
using TakeoutSaaS.Shared.Abstractions.Exceptions;
|
||||
using TakeoutSaaS.Shared.Abstractions.Security;
|
||||
@@ -26,7 +25,7 @@ public sealed class BatchIdentityUserOperationCommandHandler(
|
||||
ITenantProvider tenantProvider,
|
||||
ICurrentUserAccessor currentUserAccessor,
|
||||
IAdminAuthService adminAuthService,
|
||||
IOperationLogRepository operationLogRepository)
|
||||
IIdentityOperationLogPublisher operationLogPublisher)
|
||||
: IRequestHandler<BatchIdentityUserOperationCommand, BatchIdentityUserOperationResult>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
@@ -208,7 +207,7 @@ public sealed class BatchIdentityUserOperationCommandHandler(
|
||||
}));
|
||||
}
|
||||
|
||||
// 7. (空行后) 写入操作日志
|
||||
// 7. (空行后) 构建操作日志消息
|
||||
var operatorName = string.IsNullOrWhiteSpace(operatorProfile.DisplayName)
|
||||
? operatorProfile.Account
|
||||
: operatorProfile.DisplayName;
|
||||
@@ -217,7 +216,7 @@ public sealed class BatchIdentityUserOperationCommandHandler(
|
||||
operatorName = $"user:{currentUserAccessor.UserId}";
|
||||
}
|
||||
|
||||
var log = new OperationLog
|
||||
var logMessage = new IdentityUserOperationLogMessage
|
||||
{
|
||||
OperationType = "identity-user:batch",
|
||||
TargetType = "identity_user",
|
||||
@@ -228,8 +227,10 @@ public sealed class BatchIdentityUserOperationCommandHandler(
|
||||
Result = JsonSerializer.Serialize(new { successCount, failureCount = failures.Count }),
|
||||
Success = failures.Count == 0
|
||||
};
|
||||
await operationLogRepository.AddAsync(log, cancellationToken);
|
||||
await operationLogRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 8. (空行后) 写入 Outbox 并保存变更
|
||||
await operationLogPublisher.PublishAsync(logMessage, cancellationToken);
|
||||
await identityUserRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return new BatchIdentityUserOperationResult
|
||||
{
|
||||
|
||||
@@ -2,10 +2,9 @@ using MediatR;
|
||||
using System.Text.Json;
|
||||
using TakeoutSaaS.Application.Identity.Abstractions;
|
||||
using TakeoutSaaS.Application.Identity.Commands;
|
||||
using TakeoutSaaS.Application.Identity.Events;
|
||||
using TakeoutSaaS.Domain.Identity.Enums;
|
||||
using TakeoutSaaS.Domain.Identity.Repositories;
|
||||
using TakeoutSaaS.Domain.Tenants.Entities;
|
||||
using TakeoutSaaS.Domain.Tenants.Repositories;
|
||||
using TakeoutSaaS.Shared.Abstractions.Constants;
|
||||
using TakeoutSaaS.Shared.Abstractions.Exceptions;
|
||||
using TakeoutSaaS.Shared.Abstractions.Security;
|
||||
@@ -23,7 +22,7 @@ public sealed class ChangeIdentityUserStatusCommandHandler(
|
||||
ITenantProvider tenantProvider,
|
||||
ICurrentUserAccessor currentUserAccessor,
|
||||
IAdminAuthService adminAuthService,
|
||||
IOperationLogRepository operationLogRepository)
|
||||
IIdentityOperationLogPublisher operationLogPublisher)
|
||||
: IRequestHandler<ChangeIdentityUserStatusCommand, bool>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
@@ -81,9 +80,7 @@ public sealed class ChangeIdentityUserStatusCommandHandler(
|
||||
throw new BusinessException(ErrorCodes.BadRequest, "无效的用户状态");
|
||||
}
|
||||
|
||||
await identityUserRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 6. (空行后) 写入操作日志
|
||||
// 6. (空行后) 构建操作日志消息
|
||||
var operatorName = string.IsNullOrWhiteSpace(operatorProfile.DisplayName)
|
||||
? operatorProfile.Account
|
||||
: operatorProfile.DisplayName;
|
||||
@@ -92,7 +89,7 @@ public sealed class ChangeIdentityUserStatusCommandHandler(
|
||||
operatorName = $"user:{currentUserAccessor.UserId}";
|
||||
}
|
||||
|
||||
var log = new OperationLog
|
||||
var logMessage = new IdentityUserOperationLogMessage
|
||||
{
|
||||
OperationType = "identity-user:status-change",
|
||||
TargetType = "identity_user",
|
||||
@@ -109,8 +106,10 @@ public sealed class ChangeIdentityUserStatusCommandHandler(
|
||||
Result = JsonSerializer.Serialize(new { userId = user.Id }),
|
||||
Success = true
|
||||
};
|
||||
await operationLogRepository.AddAsync(log, cancellationToken);
|
||||
await operationLogRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 7. (空行后) 写入 Outbox 并保存变更
|
||||
await operationLogPublisher.PublishAsync(logMessage, cancellationToken);
|
||||
await identityUserRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -4,14 +4,14 @@ using System.Text.Json;
|
||||
using TakeoutSaaS.Application.Identity.Abstractions;
|
||||
using TakeoutSaaS.Application.Identity.Commands;
|
||||
using TakeoutSaaS.Application.Identity.Contracts;
|
||||
using TakeoutSaaS.Application.Identity.Events;
|
||||
using TakeoutSaaS.Application.Identity.Queries;
|
||||
using TakeoutSaaS.Domain.Identity.Entities;
|
||||
using TakeoutSaaS.Domain.Identity.Enums;
|
||||
using TakeoutSaaS.Domain.Identity.Repositories;
|
||||
using TakeoutSaaS.Domain.Tenants.Entities;
|
||||
using TakeoutSaaS.Domain.Tenants.Repositories;
|
||||
using TakeoutSaaS.Shared.Abstractions.Constants;
|
||||
using TakeoutSaaS.Shared.Abstractions.Exceptions;
|
||||
using TakeoutSaaS.Shared.Abstractions.Ids;
|
||||
using TakeoutSaaS.Shared.Abstractions.Security;
|
||||
using TakeoutSaaS.Shared.Abstractions.Tenancy;
|
||||
|
||||
@@ -28,7 +28,8 @@ public sealed class CreateIdentityUserCommandHandler(
|
||||
ITenantProvider tenantProvider,
|
||||
ICurrentUserAccessor currentUserAccessor,
|
||||
IAdminAuthService adminAuthService,
|
||||
IOperationLogRepository operationLogRepository,
|
||||
IIdentityOperationLogPublisher operationLogPublisher,
|
||||
IIdGenerator idGenerator,
|
||||
IMediator mediator)
|
||||
: IRequestHandler<CreateIdentityUserCommand, UserDetailDto>
|
||||
{
|
||||
@@ -85,6 +86,7 @@ public sealed class CreateIdentityUserCommandHandler(
|
||||
// 6. (空行后) 创建用户实体
|
||||
var user = new IdentityUser
|
||||
{
|
||||
Id = idGenerator.NextId(),
|
||||
TenantId = tenantId,
|
||||
Account = account,
|
||||
DisplayName = displayName,
|
||||
@@ -100,17 +102,7 @@ public sealed class CreateIdentityUserCommandHandler(
|
||||
};
|
||||
user.PasswordHash = passwordHasher.HashPassword(user, request.Password);
|
||||
|
||||
// 7. (空行后) 持久化用户
|
||||
await identityUserRepository.AddAsync(user, cancellationToken);
|
||||
await identityUserRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 8. (空行后) 绑定角色
|
||||
if (roleIds.Length > 0)
|
||||
{
|
||||
await userRoleRepository.ReplaceUserRolesAsync(tenantId, user.Id, roleIds, cancellationToken);
|
||||
}
|
||||
|
||||
// 9. (空行后) 写入操作日志
|
||||
// 7. (空行后) 构建操作日志消息
|
||||
var operatorName = string.IsNullOrWhiteSpace(operatorProfile.DisplayName)
|
||||
? operatorProfile.Account
|
||||
: operatorProfile.DisplayName;
|
||||
@@ -119,7 +111,7 @@ public sealed class CreateIdentityUserCommandHandler(
|
||||
operatorName = $"user:{currentUserAccessor.UserId}";
|
||||
}
|
||||
|
||||
var log = new OperationLog
|
||||
var logMessage = new IdentityUserOperationLogMessage
|
||||
{
|
||||
OperationType = "identity-user:create",
|
||||
TargetType = "identity_user",
|
||||
@@ -138,8 +130,17 @@ public sealed class CreateIdentityUserCommandHandler(
|
||||
Result = JsonSerializer.Serialize(new { userId = user.Id }),
|
||||
Success = true
|
||||
};
|
||||
await operationLogRepository.AddAsync(log, cancellationToken);
|
||||
await operationLogRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 8. (空行后) 持久化用户并写入 Outbox
|
||||
await identityUserRepository.AddAsync(user, cancellationToken);
|
||||
await operationLogPublisher.PublishAsync(logMessage, cancellationToken);
|
||||
await identityUserRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 9. (空行后) 绑定角色
|
||||
if (roleIds.Length > 0)
|
||||
{
|
||||
await userRoleRepository.ReplaceUserRolesAsync(tenantId, user.Id, roleIds, cancellationToken);
|
||||
}
|
||||
|
||||
// 10. (空行后) 返回用户详情
|
||||
var detail = await mediator.Send(new GetIdentityUserDetailQuery { UserId = user.Id }, cancellationToken);
|
||||
|
||||
@@ -2,10 +2,9 @@ using MediatR;
|
||||
using System.Text.Json;
|
||||
using TakeoutSaaS.Application.Identity.Abstractions;
|
||||
using TakeoutSaaS.Application.Identity.Commands;
|
||||
using TakeoutSaaS.Application.Identity.Events;
|
||||
using TakeoutSaaS.Domain.Identity.Enums;
|
||||
using TakeoutSaaS.Domain.Identity.Repositories;
|
||||
using TakeoutSaaS.Domain.Tenants.Entities;
|
||||
using TakeoutSaaS.Domain.Tenants.Repositories;
|
||||
using TakeoutSaaS.Shared.Abstractions.Constants;
|
||||
using TakeoutSaaS.Shared.Abstractions.Exceptions;
|
||||
using TakeoutSaaS.Shared.Abstractions.Security;
|
||||
@@ -23,7 +22,7 @@ public sealed class DeleteIdentityUserCommandHandler(
|
||||
ITenantProvider tenantProvider,
|
||||
ICurrentUserAccessor currentUserAccessor,
|
||||
IAdminAuthService adminAuthService,
|
||||
IOperationLogRepository operationLogRepository)
|
||||
IIdentityOperationLogPublisher operationLogPublisher)
|
||||
: IRequestHandler<DeleteIdentityUserCommand, bool>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
@@ -60,11 +59,7 @@ public sealed class DeleteIdentityUserCommandHandler(
|
||||
await EnsureNotLastActiveTenantAdminAsync(user.TenantId, user.Id, isSuperAdmin, cancellationToken);
|
||||
}
|
||||
|
||||
// 5. (空行后) 软删除用户
|
||||
await identityUserRepository.RemoveAsync(user, cancellationToken);
|
||||
await identityUserRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 6. (空行后) 写入操作日志
|
||||
// 5. (空行后) 构建操作日志消息
|
||||
var operatorName = string.IsNullOrWhiteSpace(operatorProfile.DisplayName)
|
||||
? operatorProfile.Account
|
||||
: operatorProfile.DisplayName;
|
||||
@@ -73,7 +68,7 @@ public sealed class DeleteIdentityUserCommandHandler(
|
||||
operatorName = $"user:{currentUserAccessor.UserId}";
|
||||
}
|
||||
|
||||
var log = new OperationLog
|
||||
var logMessage = new IdentityUserOperationLogMessage
|
||||
{
|
||||
OperationType = "identity-user:delete",
|
||||
TargetType = "identity_user",
|
||||
@@ -84,8 +79,11 @@ public sealed class DeleteIdentityUserCommandHandler(
|
||||
Result = JsonSerializer.Serialize(new { userId = user.Id }),
|
||||
Success = true
|
||||
};
|
||||
await operationLogRepository.AddAsync(log, cancellationToken);
|
||||
await operationLogRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 6. (空行后) 软删除用户并写入 Outbox
|
||||
await identityUserRepository.RemoveAsync(user, cancellationToken);
|
||||
await operationLogPublisher.PublishAsync(logMessage, cancellationToken);
|
||||
await identityUserRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3,10 +3,9 @@ using System.Text.Json;
|
||||
using TakeoutSaaS.Application.Identity.Abstractions;
|
||||
using TakeoutSaaS.Application.Identity.Commands;
|
||||
using TakeoutSaaS.Application.Identity.Contracts;
|
||||
using TakeoutSaaS.Application.Identity.Events;
|
||||
using TakeoutSaaS.Domain.Identity.Enums;
|
||||
using TakeoutSaaS.Domain.Identity.Repositories;
|
||||
using TakeoutSaaS.Domain.Tenants.Entities;
|
||||
using TakeoutSaaS.Domain.Tenants.Repositories;
|
||||
using TakeoutSaaS.Shared.Abstractions.Constants;
|
||||
using TakeoutSaaS.Shared.Abstractions.Exceptions;
|
||||
using TakeoutSaaS.Shared.Abstractions.Security;
|
||||
@@ -23,7 +22,7 @@ public sealed class ResetIdentityUserPasswordCommandHandler(
|
||||
ITenantProvider tenantProvider,
|
||||
ICurrentUserAccessor currentUserAccessor,
|
||||
IAdminAuthService adminAuthService,
|
||||
IOperationLogRepository operationLogRepository)
|
||||
IIdentityOperationLogPublisher operationLogPublisher)
|
||||
: IRequestHandler<ResetIdentityUserPasswordCommand, ResetIdentityUserPasswordResult>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
@@ -66,9 +65,8 @@ public sealed class ResetIdentityUserPasswordCommandHandler(
|
||||
{
|
||||
user.Status = IdentityUserStatus.Active;
|
||||
}
|
||||
await identityUserRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 6. (空行后) 写入操作日志
|
||||
// 6. (空行后) 构建操作日志消息
|
||||
var operatorName = string.IsNullOrWhiteSpace(operatorProfile.DisplayName)
|
||||
? operatorProfile.Account
|
||||
: operatorProfile.DisplayName;
|
||||
@@ -77,7 +75,7 @@ public sealed class ResetIdentityUserPasswordCommandHandler(
|
||||
operatorName = $"user:{currentUserAccessor.UserId}";
|
||||
}
|
||||
|
||||
var log = new OperationLog
|
||||
var logMessage = new IdentityUserOperationLogMessage
|
||||
{
|
||||
OperationType = "identity-user:password-reset",
|
||||
TargetType = "identity_user",
|
||||
@@ -88,8 +86,10 @@ public sealed class ResetIdentityUserPasswordCommandHandler(
|
||||
Result = JsonSerializer.Serialize(new { userId = user.Id, expiresAt }),
|
||||
Success = true
|
||||
};
|
||||
await operationLogRepository.AddAsync(log, cancellationToken);
|
||||
await operationLogRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 7. (空行后) 写入 Outbox 并保存变更
|
||||
await operationLogPublisher.PublishAsync(logMessage, cancellationToken);
|
||||
await identityUserRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return new ResetIdentityUserPasswordResult
|
||||
{
|
||||
|
||||
@@ -2,9 +2,8 @@ using MediatR;
|
||||
using System.Text.Json;
|
||||
using TakeoutSaaS.Application.Identity.Abstractions;
|
||||
using TakeoutSaaS.Application.Identity.Commands;
|
||||
using TakeoutSaaS.Application.Identity.Events;
|
||||
using TakeoutSaaS.Domain.Identity.Repositories;
|
||||
using TakeoutSaaS.Domain.Tenants.Entities;
|
||||
using TakeoutSaaS.Domain.Tenants.Repositories;
|
||||
using TakeoutSaaS.Shared.Abstractions.Constants;
|
||||
using TakeoutSaaS.Shared.Abstractions.Exceptions;
|
||||
using TakeoutSaaS.Shared.Abstractions.Security;
|
||||
@@ -20,7 +19,7 @@ public sealed class RestoreIdentityUserCommandHandler(
|
||||
ITenantProvider tenantProvider,
|
||||
ICurrentUserAccessor currentUserAccessor,
|
||||
IAdminAuthService adminAuthService,
|
||||
IOperationLogRepository operationLogRepository)
|
||||
IIdentityOperationLogPublisher operationLogPublisher)
|
||||
: IRequestHandler<RestoreIdentityUserCommand, bool>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
@@ -54,12 +53,7 @@ public sealed class RestoreIdentityUserCommandHandler(
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4. (空行后) 恢复软删除状态
|
||||
user.DeletedAt = null;
|
||||
user.DeletedBy = null;
|
||||
await identityUserRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 5. (空行后) 写入操作日志
|
||||
// 4. (空行后) 构建操作日志消息
|
||||
var operatorName = string.IsNullOrWhiteSpace(operatorProfile.DisplayName)
|
||||
? operatorProfile.Account
|
||||
: operatorProfile.DisplayName;
|
||||
@@ -68,7 +62,7 @@ public sealed class RestoreIdentityUserCommandHandler(
|
||||
operatorName = $"user:{currentUserAccessor.UserId}";
|
||||
}
|
||||
|
||||
var log = new OperationLog
|
||||
var logMessage = new IdentityUserOperationLogMessage
|
||||
{
|
||||
OperationType = "identity-user:restore",
|
||||
TargetType = "identity_user",
|
||||
@@ -79,8 +73,12 @@ public sealed class RestoreIdentityUserCommandHandler(
|
||||
Result = JsonSerializer.Serialize(new { userId = user.Id }),
|
||||
Success = true
|
||||
};
|
||||
await operationLogRepository.AddAsync(log, cancellationToken);
|
||||
await operationLogRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 5. (空行后) 恢复软删除状态并写入 Outbox
|
||||
user.DeletedAt = null;
|
||||
user.DeletedBy = null;
|
||||
await operationLogPublisher.PublishAsync(logMessage, cancellationToken);
|
||||
await identityUserRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3,11 +3,10 @@ using System.Text.Json;
|
||||
using TakeoutSaaS.Application.Identity.Abstractions;
|
||||
using TakeoutSaaS.Application.Identity.Commands;
|
||||
using TakeoutSaaS.Application.Identity.Contracts;
|
||||
using TakeoutSaaS.Application.Identity.Events;
|
||||
using TakeoutSaaS.Application.Identity.Queries;
|
||||
using TakeoutSaaS.Domain.Identity.Entities;
|
||||
using TakeoutSaaS.Domain.Identity.Repositories;
|
||||
using TakeoutSaaS.Domain.Tenants.Entities;
|
||||
using TakeoutSaaS.Domain.Tenants.Repositories;
|
||||
using TakeoutSaaS.Shared.Abstractions.Constants;
|
||||
using TakeoutSaaS.Shared.Abstractions.Exceptions;
|
||||
using TakeoutSaaS.Shared.Abstractions.Security;
|
||||
@@ -25,7 +24,7 @@ public sealed class UpdateIdentityUserCommandHandler(
|
||||
ITenantProvider tenantProvider,
|
||||
ICurrentUserAccessor currentUserAccessor,
|
||||
IAdminAuthService adminAuthService,
|
||||
IOperationLogRepository operationLogRepository,
|
||||
IIdentityOperationLogPublisher operationLogPublisher,
|
||||
IMediator mediator)
|
||||
: IRequestHandler<UpdateIdentityUserCommand, UserDetailDto?>
|
||||
{
|
||||
@@ -93,23 +92,7 @@ public sealed class UpdateIdentityUserCommandHandler(
|
||||
user.Avatar = string.IsNullOrWhiteSpace(request.Avatar) ? null : request.Avatar.Trim();
|
||||
user.RowVersion = request.RowVersion;
|
||||
|
||||
// 6. (空行后) 持久化用户更新
|
||||
try
|
||||
{
|
||||
await identityUserRepository.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
catch (Exception ex) when (IsConcurrencyException(ex))
|
||||
{
|
||||
throw new BusinessException(ErrorCodes.Conflict, "用户数据已被修改,请刷新后重试");
|
||||
}
|
||||
|
||||
// 7. (空行后) 覆盖角色绑定(仅当显式传入时)
|
||||
if (roleIds != null)
|
||||
{
|
||||
await userRoleRepository.ReplaceUserRolesAsync(user.TenantId, user.Id, roleIds, cancellationToken);
|
||||
}
|
||||
|
||||
// 8. (空行后) 写入操作日志
|
||||
// 6. (空行后) 构建操作日志消息
|
||||
var operatorName = string.IsNullOrWhiteSpace(operatorProfile.DisplayName)
|
||||
? operatorProfile.Account
|
||||
: operatorProfile.DisplayName;
|
||||
@@ -118,7 +101,7 @@ public sealed class UpdateIdentityUserCommandHandler(
|
||||
operatorName = $"user:{currentUserAccessor.UserId}";
|
||||
}
|
||||
|
||||
var log = new OperationLog
|
||||
var logMessage = new IdentityUserOperationLogMessage
|
||||
{
|
||||
OperationType = "identity-user:update",
|
||||
TargetType = "identity_user",
|
||||
@@ -136,8 +119,23 @@ public sealed class UpdateIdentityUserCommandHandler(
|
||||
Result = JsonSerializer.Serialize(new { userId = user.Id }),
|
||||
Success = true
|
||||
};
|
||||
await operationLogRepository.AddAsync(log, cancellationToken);
|
||||
await operationLogRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 7. (空行后) 持久化用户更新并写入 Outbox
|
||||
try
|
||||
{
|
||||
await operationLogPublisher.PublishAsync(logMessage, cancellationToken);
|
||||
await identityUserRepository.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
catch (Exception ex) when (IsConcurrencyException(ex))
|
||||
{
|
||||
throw new BusinessException(ErrorCodes.Conflict, "用户数据已被修改,请刷新后重试");
|
||||
}
|
||||
|
||||
// 8. (空行后) 覆盖角色绑定(仅当显式传入时)
|
||||
if (roleIds != null)
|
||||
{
|
||||
await userRoleRepository.ReplaceUserRolesAsync(user.TenantId, user.Id, roleIds, cancellationToken);
|
||||
}
|
||||
|
||||
// 9. (空行后) 返回用户详情
|
||||
return await mediator.Send(new GetIdentityUserDetailQuery { UserId = user.Id }, cancellationToken);
|
||||
|
||||
@@ -8,6 +8,7 @@ using TakeoutSaaS.Infrastructure.Identity.Options;
|
||||
using TakeoutSaaS.Infrastructure.Identity.Persistence;
|
||||
using TakeoutSaaS.Infrastructure.Identity.Repositories;
|
||||
using TakeoutSaaS.Infrastructure.Identity.Services;
|
||||
using TakeoutSaaS.Infrastructure.Logs.Publishers;
|
||||
using TakeoutSaaS.Shared.Abstractions.Constants;
|
||||
using DomainIdentityUser = TakeoutSaaS.Domain.Identity.Entities.IdentityUser;
|
||||
|
||||
@@ -60,6 +61,7 @@ public static class ServiceCollectionExtensions
|
||||
services.AddScoped<IAdminPasswordResetTokenStore, RedisAdminPasswordResetTokenStore>();
|
||||
services.AddScoped<ILoginRateLimiter, RedisLoginRateLimiter>();
|
||||
services.AddScoped<IPasswordHasher<DomainIdentityUser>, PasswordHasher<DomainIdentityUser>>();
|
||||
services.AddScoped<IIdentityOperationLogPublisher, IdentityOperationLogPublisher>();
|
||||
|
||||
services.AddOptions<JwtOptions>()
|
||||
.Bind(configuration.GetSection("Identity:Jwt"))
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using MassTransit;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using TakeoutSaaS.Domain.Identity.Entities;
|
||||
@@ -79,6 +80,8 @@ public sealed class IdentityDbContext(
|
||||
ConfigureUserRole(modelBuilder.Entity<UserRole>());
|
||||
ConfigureRolePermission(modelBuilder.Entity<RolePermission>());
|
||||
ConfigureMenuDefinition(modelBuilder.Entity<MenuDefinition>());
|
||||
modelBuilder.AddOutboxMessageEntity();
|
||||
modelBuilder.AddOutboxStateEntity();
|
||||
ApplyTenantQueryFilters(modelBuilder);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
using MassTransit;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Npgsql;
|
||||
using TakeoutSaaS.Application.Identity.Events;
|
||||
using TakeoutSaaS.Domain.Tenants.Entities;
|
||||
using TakeoutSaaS.Infrastructure.Logs.Persistence;
|
||||
|
||||
namespace TakeoutSaaS.Infrastructure.Logs.Consumers;
|
||||
|
||||
/// <summary>
|
||||
/// 身份用户操作日志消费者。
|
||||
/// </summary>
|
||||
public sealed class IdentityUserOperationLogConsumer(TakeoutLogsDbContext logsContext) : IConsumer<IdentityUserOperationLogMessage>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public async Task Consume(ConsumeContext<IdentityUserOperationLogMessage> context)
|
||||
{
|
||||
// 1. 校验消息标识并进行幂等检查
|
||||
var messageId = context.MessageId;
|
||||
if (!messageId.HasValue)
|
||||
{
|
||||
throw new InvalidOperationException("缺少 MessageId,无法进行日志幂等处理。");
|
||||
}
|
||||
|
||||
var exists = await logsContext.OperationLogInboxMessages
|
||||
.AsNoTracking()
|
||||
.AnyAsync(x => x.MessageId == messageId.Value, context.CancellationToken);
|
||||
if (exists)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. (空行后) 构建日志实体与去重记录
|
||||
var message = context.Message;
|
||||
var log = new OperationLog
|
||||
{
|
||||
OperationType = message.OperationType,
|
||||
TargetType = message.TargetType,
|
||||
TargetIds = message.TargetIds,
|
||||
OperatorId = message.OperatorId,
|
||||
OperatorName = message.OperatorName,
|
||||
Parameters = message.Parameters,
|
||||
Result = message.Result,
|
||||
Success = message.Success
|
||||
};
|
||||
logsContext.OperationLogInboxMessages.Add(new OperationLogInboxMessage
|
||||
{
|
||||
MessageId = messageId.Value,
|
||||
ConsumedAt = DateTime.UtcNow
|
||||
});
|
||||
logsContext.OperationLogs.Add(log);
|
||||
|
||||
// 3. (空行后) 保存并处理并发去重冲突
|
||||
try
|
||||
{
|
||||
await logsContext.SaveChangesAsync(context.CancellationToken);
|
||||
}
|
||||
catch (DbUpdateException ex) when (IsDuplicateMessage(ex))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsDuplicateMessage(DbUpdateException exception)
|
||||
{
|
||||
if (exception.InnerException is PostgresException postgresException)
|
||||
{
|
||||
return postgresException.SqlState == PostgresErrorCodes.UniqueViolation;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using MassTransit;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using TakeoutSaaS.Infrastructure.Identity.Persistence;
|
||||
using TakeoutSaaS.Infrastructure.Logs.Consumers;
|
||||
using TakeoutSaaS.Module.Messaging.Options;
|
||||
|
||||
namespace TakeoutSaaS.Infrastructure.Logs.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// 操作日志 Outbox 注册扩展。
|
||||
/// </summary>
|
||||
public static class OperationLogOutboxServiceCollectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 注册操作日志 Outbox 与消费者。
|
||||
/// </summary>
|
||||
/// <param name="services">服务集合。</param>
|
||||
/// <param name="configuration">配置源。</param>
|
||||
/// <returns>服务集合。</returns>
|
||||
public static IServiceCollection AddOperationLogOutbox(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
// 1. 读取 RabbitMQ 配置
|
||||
var options = configuration.GetSection("RabbitMQ").Get<RabbitMqOptions>();
|
||||
if (options == null)
|
||||
{
|
||||
throw new InvalidOperationException("缺少 RabbitMQ 配置。");
|
||||
}
|
||||
|
||||
// 2. (空行后) 注册 MassTransit 与 Outbox
|
||||
services.AddMassTransit(configurator =>
|
||||
{
|
||||
configurator.AddConsumer<IdentityUserOperationLogConsumer>();
|
||||
configurator.AddEntityFrameworkOutbox<IdentityDbContext>(outbox =>
|
||||
{
|
||||
outbox.UsePostgres();
|
||||
outbox.UseBusOutbox();
|
||||
});
|
||||
configurator.UsingRabbitMq((context, cfg) =>
|
||||
{
|
||||
var virtualHost = string.IsNullOrWhiteSpace(options.VirtualHost) ? "/" : options.VirtualHost.Trim();
|
||||
var virtualHostPath = virtualHost == "/" ? "/" : $"/{virtualHost.TrimStart('/')}";
|
||||
var hostUri = new Uri($"rabbitmq://{options.Host}:{options.Port}{virtualHostPath}");
|
||||
cfg.Host(hostUri, host =>
|
||||
{
|
||||
host.Username(options.Username);
|
||||
host.Password(options.Password);
|
||||
});
|
||||
cfg.PrefetchCount = options.PrefetchCount;
|
||||
cfg.ConfigureEndpoints(context);
|
||||
});
|
||||
});
|
||||
// 3. (空行后) 返回服务集合
|
||||
return services;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using TakeoutSaaS.Shared.Abstractions.Entities;
|
||||
|
||||
namespace TakeoutSaaS.Infrastructure.Logs.Persistence;
|
||||
|
||||
/// <summary>
|
||||
/// 操作日志消息消费去重记录。
|
||||
/// </summary>
|
||||
public sealed class OperationLogInboxMessage : EntityBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 消息唯一标识。
|
||||
/// </summary>
|
||||
public Guid MessageId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 消费时间(UTC)。
|
||||
/// </summary>
|
||||
public DateTime ConsumedAt { get; set; }
|
||||
}
|
||||
@@ -35,6 +35,11 @@ public sealed class TakeoutLogsDbContext(
|
||||
/// </summary>
|
||||
public DbSet<OperationLog> OperationLogs => Set<OperationLog>();
|
||||
|
||||
/// <summary>
|
||||
/// 操作日志消息去重集合。
|
||||
/// </summary>
|
||||
public DbSet<OperationLogInboxMessage> OperationLogInboxMessages => Set<OperationLogInboxMessage>();
|
||||
|
||||
/// <summary>
|
||||
/// 成长值日志集合。
|
||||
/// </summary>
|
||||
@@ -50,6 +55,7 @@ public sealed class TakeoutLogsDbContext(
|
||||
ConfigureTenantAuditLog(modelBuilder.Entity<TenantAuditLog>());
|
||||
ConfigureMerchantAuditLog(modelBuilder.Entity<MerchantAuditLog>());
|
||||
ConfigureOperationLog(modelBuilder.Entity<OperationLog>());
|
||||
ConfigureOperationLogInboxMessage(modelBuilder.Entity<OperationLogInboxMessage>());
|
||||
ConfigureMemberGrowthLog(modelBuilder.Entity<MemberGrowthLog>());
|
||||
}
|
||||
|
||||
@@ -91,6 +97,15 @@ public sealed class TakeoutLogsDbContext(
|
||||
builder.HasIndex(x => x.CreatedAt);
|
||||
}
|
||||
|
||||
private static void ConfigureOperationLogInboxMessage(EntityTypeBuilder<OperationLogInboxMessage> builder)
|
||||
{
|
||||
builder.ToTable("operation_log_inbox_messages");
|
||||
builder.HasKey(x => x.Id);
|
||||
builder.Property(x => x.MessageId).IsRequired();
|
||||
builder.Property(x => x.ConsumedAt).IsRequired();
|
||||
builder.HasIndex(x => x.MessageId).IsUnique();
|
||||
}
|
||||
|
||||
private static void ConfigureMemberGrowthLog(EntityTypeBuilder<MemberGrowthLog> builder)
|
||||
{
|
||||
builder.ToTable("member_growth_logs");
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
using MassTransit;
|
||||
using TakeoutSaaS.Application.Identity.Abstractions;
|
||||
using TakeoutSaaS.Application.Identity.Events;
|
||||
|
||||
namespace TakeoutSaaS.Infrastructure.Logs.Publishers;
|
||||
|
||||
/// <summary>
|
||||
/// 身份模块操作日志发布器(基于 MassTransit Outbox)。
|
||||
/// </summary>
|
||||
public sealed class IdentityOperationLogPublisher(IPublishEndpoint publishEndpoint) : IIdentityOperationLogPublisher
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public Task PublishAsync(IdentityUserOperationLogMessage message, CancellationToken cancellationToken = default)
|
||||
=> publishEndpoint.Publish(message, cancellationToken);
|
||||
}
|
||||
@@ -0,0 +1,847 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
using TakeoutSaaS.Infrastructure.Identity.Persistence;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace TakeoutSaaS.Infrastructure.Migrations.IdentityDb
|
||||
{
|
||||
[DbContext(typeof(IdentityDbContext))]
|
||||
[Migration("20251227004313_AddIdentityOutbox")]
|
||||
partial class AddIdentityOutbox
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "10.0.0")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("MassTransit.EntityFrameworkCoreIntegration.OutboxMessage", b =>
|
||||
{
|
||||
b.Property<long>("SequenceNumber")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("SequenceNumber"));
|
||||
|
||||
b.Property<string>("Body")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ContentType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<Guid?>("ConversationId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid?>("CorrelationId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("DestinationAddress")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<DateTime?>("EnqueueTime")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime?>("ExpirationTime")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("FaultAddress")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<string>("Headers")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<Guid?>("InboxConsumerId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid?>("InboxMessageId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid?>("InitiatorId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("MessageId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("MessageType")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<Guid?>("OutboxId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Properties")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<Guid?>("RequestId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("ResponseAddress")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<DateTime>("SentTime")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("SourceAddress")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.HasKey("SequenceNumber");
|
||||
|
||||
b.HasIndex("EnqueueTime");
|
||||
|
||||
b.HasIndex("ExpirationTime");
|
||||
|
||||
b.HasIndex("OutboxId", "SequenceNumber")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("InboxMessageId", "InboxConsumerId", "SequenceNumber")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("OutboxMessage");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MassTransit.EntityFrameworkCoreIntegration.OutboxState", b =>
|
||||
{
|
||||
b.Property<Guid>("OutboxId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("Created")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime?>("Delivered")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<long?>("LastSequenceNumber")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<Guid>("LockId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<byte[]>("RowVersion")
|
||||
.IsConcurrencyToken()
|
||||
.ValueGeneratedOnAddOrUpdate()
|
||||
.HasColumnType("bytea");
|
||||
|
||||
b.HasKey("OutboxId");
|
||||
|
||||
b.HasIndex("Created");
|
||||
|
||||
b.ToTable("OutboxState");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TakeoutSaaS.Domain.Identity.Entities.IdentityUser", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("实体唯一标识。");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<string>("Account")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)")
|
||||
.HasComment("登录账号。");
|
||||
|
||||
b.Property<string>("Avatar")
|
||||
.HasColumnType("text")
|
||||
.HasComment("头像地址。");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("创建时间(UTC)。");
|
||||
|
||||
b.Property<long?>("CreatedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("创建人用户标识,匿名或系统操作时为 null。");
|
||||
|
||||
b.Property<DateTime?>("DeletedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("软删除时间(UTC),未删除时为 null。");
|
||||
|
||||
b.Property<long?>("DeletedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("删除人用户标识(软删除),未删除时为 null。");
|
||||
|
||||
b.Property<string>("DisplayName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)")
|
||||
.HasComment("展示名称。");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)")
|
||||
.HasComment("邮箱(租户内唯一)。");
|
||||
|
||||
b.Property<int>("FailedLoginCount")
|
||||
.HasColumnType("integer")
|
||||
.HasComment("登录失败次数。");
|
||||
|
||||
b.Property<DateTime?>("LastLoginAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("最近登录时间(UTC)。");
|
||||
|
||||
b.Property<DateTime?>("LockedUntil")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("锁定截止时间(UTC)。");
|
||||
|
||||
b.Property<long?>("MerchantId")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("所属商户(平台管理员为空)。");
|
||||
|
||||
b.Property<bool>("MustChangePassword")
|
||||
.HasColumnType("boolean")
|
||||
.HasComment("是否强制修改密码。");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)")
|
||||
.HasComment("密码哈希。");
|
||||
|
||||
b.Property<string>("Phone")
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasComment("手机号(租户内唯一)。");
|
||||
|
||||
b.Property<byte[]>("RowVersion")
|
||||
.IsConcurrencyToken()
|
||||
.IsRequired()
|
||||
.ValueGeneratedOnAddOrUpdate()
|
||||
.HasColumnType("bytea")
|
||||
.HasComment("并发控制字段。");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("integer")
|
||||
.HasComment("账号状态。");
|
||||
|
||||
b.Property<long>("TenantId")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("所属租户 ID。");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("最近一次更新时间(UTC),从未更新时为 null。");
|
||||
|
||||
b.Property<long?>("UpdatedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("最后更新人用户标识,匿名或系统操作时为 null。");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
|
||||
b.HasIndex("TenantId", "Account")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("TenantId", "Email")
|
||||
.IsUnique()
|
||||
.HasFilter("\"Email\" IS NOT NULL");
|
||||
|
||||
b.HasIndex("TenantId", "Phone")
|
||||
.IsUnique()
|
||||
.HasFilter("\"Phone\" IS NOT NULL");
|
||||
|
||||
b.ToTable("identity_users", null, t =>
|
||||
{
|
||||
t.HasComment("管理后台账户实体(平台管理员、租户管理员或商户员工)。");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TakeoutSaaS.Domain.Identity.Entities.MenuDefinition", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("实体唯一标识。");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<string>("AuthListJson")
|
||||
.HasColumnType("text")
|
||||
.HasComment("按钮权限列表 JSON(存储 MenuAuthItemDto 数组)。");
|
||||
|
||||
b.Property<string>("Component")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)")
|
||||
.HasComment("组件路径(不含 .vue)。");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("创建时间(UTC)。");
|
||||
|
||||
b.Property<long?>("CreatedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("创建人用户标识,匿名或系统操作时为 null。");
|
||||
|
||||
b.Property<DateTime?>("DeletedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("软删除时间(UTC),未删除时为 null。");
|
||||
|
||||
b.Property<long?>("DeletedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("删除人用户标识(软删除),未删除时为 null。");
|
||||
|
||||
b.Property<string>("Icon")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)")
|
||||
.HasComment("图标标识。");
|
||||
|
||||
b.Property<bool>("IsIframe")
|
||||
.HasColumnType("boolean")
|
||||
.HasComment("是否 iframe。");
|
||||
|
||||
b.Property<bool>("KeepAlive")
|
||||
.HasColumnType("boolean")
|
||||
.HasComment("是否缓存。");
|
||||
|
||||
b.Property<string>("Link")
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("character varying(512)")
|
||||
.HasComment("外链或 iframe 地址。");
|
||||
|
||||
b.Property<string>("MetaPermissions")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("character varying(1024)")
|
||||
.HasComment("Meta.permissions(逗号分隔)。");
|
||||
|
||||
b.Property<string>("MetaRoles")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("character varying(1024)")
|
||||
.HasComment("Meta.roles(逗号分隔)。");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)")
|
||||
.HasComment("菜单名称(前端路由 name)。");
|
||||
|
||||
b.Property<long>("ParentId")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("父级菜单 ID,根节点为 0。");
|
||||
|
||||
b.Property<string>("Path")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)")
|
||||
.HasComment("路由路径。");
|
||||
|
||||
b.Property<string>("RequiredPermissions")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("character varying(1024)")
|
||||
.HasComment("访问该菜单所需的权限集合(逗号分隔)。");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("integer")
|
||||
.HasComment("排序。");
|
||||
|
||||
b.Property<long>("TenantId")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("所属租户 ID。");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)")
|
||||
.HasComment("标题。");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("最近一次更新时间(UTC),从未更新时为 null。");
|
||||
|
||||
b.Property<long?>("UpdatedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("最后更新人用户标识,匿名或系统操作时为 null。");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TenantId", "ParentId", "SortOrder");
|
||||
|
||||
b.ToTable("menu_definitions", null, t =>
|
||||
{
|
||||
t.HasComment("管理端菜单定义。");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TakeoutSaaS.Domain.Identity.Entities.MiniUser", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("实体唯一标识。");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<string>("Avatar")
|
||||
.HasColumnType("text")
|
||||
.HasComment("头像地址。");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("创建时间(UTC)。");
|
||||
|
||||
b.Property<long?>("CreatedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("创建人用户标识,匿名或系统操作时为 null。");
|
||||
|
||||
b.Property<DateTime?>("DeletedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("软删除时间(UTC),未删除时为 null。");
|
||||
|
||||
b.Property<long?>("DeletedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("删除人用户标识(软删除),未删除时为 null。");
|
||||
|
||||
b.Property<string>("Nickname")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)")
|
||||
.HasComment("昵称。");
|
||||
|
||||
b.Property<string>("OpenId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)")
|
||||
.HasComment("微信 OpenId。");
|
||||
|
||||
b.Property<long>("TenantId")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("所属租户 ID。");
|
||||
|
||||
b.Property<string>("UnionId")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)")
|
||||
.HasComment("微信 UnionId,可能为空。");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("最近一次更新时间(UTC),从未更新时为 null。");
|
||||
|
||||
b.Property<long?>("UpdatedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("最后更新人用户标识,匿名或系统操作时为 null。");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
|
||||
b.HasIndex("TenantId", "OpenId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("mini_users", null, t =>
|
||||
{
|
||||
t.HasComment("小程序用户实体。");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TakeoutSaaS.Domain.Identity.Entities.Permission", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("实体唯一标识。");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<string>("Code")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)")
|
||||
.HasComment("权限编码(租户内唯一)。");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("创建时间(UTC)。");
|
||||
|
||||
b.Property<long?>("CreatedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("创建人用户标识,匿名或系统操作时为 null。");
|
||||
|
||||
b.Property<DateTime?>("DeletedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("软删除时间(UTC),未删除时为 null。");
|
||||
|
||||
b.Property<long?>("DeletedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("删除人用户标识(软删除),未删除时为 null。");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)")
|
||||
.HasComment("描述。");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)")
|
||||
.HasComment("权限名称。");
|
||||
|
||||
b.Property<long>("ParentId")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("父级权限 ID,根节点为 0。");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("integer")
|
||||
.HasComment("排序值,值越小越靠前。");
|
||||
|
||||
b.Property<long>("TenantId")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("所属租户 ID。");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(16)
|
||||
.HasColumnType("character varying(16)")
|
||||
.HasComment("权限类型(group/leaf)。");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("最近一次更新时间(UTC),从未更新时为 null。");
|
||||
|
||||
b.Property<long?>("UpdatedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("最后更新人用户标识,匿名或系统操作时为 null。");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
|
||||
b.HasIndex("TenantId", "Code")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("TenantId", "ParentId", "SortOrder");
|
||||
|
||||
b.ToTable("permissions", null, t =>
|
||||
{
|
||||
t.HasComment("权限定义。");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TakeoutSaaS.Domain.Identity.Entities.Role", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("实体唯一标识。");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<string>("Code")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)")
|
||||
.HasComment("角色编码(租户内唯一)。");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("创建时间(UTC)。");
|
||||
|
||||
b.Property<long?>("CreatedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("创建人用户标识,匿名或系统操作时为 null。");
|
||||
|
||||
b.Property<DateTime?>("DeletedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("软删除时间(UTC),未删除时为 null。");
|
||||
|
||||
b.Property<long?>("DeletedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("删除人用户标识(软删除),未删除时为 null。");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)")
|
||||
.HasComment("描述。");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)")
|
||||
.HasComment("角色名称。");
|
||||
|
||||
b.Property<long>("TenantId")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("所属租户 ID。");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("最近一次更新时间(UTC),从未更新时为 null。");
|
||||
|
||||
b.Property<long?>("UpdatedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("最后更新人用户标识,匿名或系统操作时为 null。");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
|
||||
b.HasIndex("TenantId", "Code")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("roles", null, t =>
|
||||
{
|
||||
t.HasComment("角色定义。");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TakeoutSaaS.Domain.Identity.Entities.RolePermission", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("实体唯一标识。");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("创建时间(UTC)。");
|
||||
|
||||
b.Property<long?>("CreatedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("创建人用户标识,匿名或系统操作时为 null。");
|
||||
|
||||
b.Property<DateTime?>("DeletedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("软删除时间(UTC),未删除时为 null。");
|
||||
|
||||
b.Property<long?>("DeletedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("删除人用户标识(软删除),未删除时为 null。");
|
||||
|
||||
b.Property<long>("PermissionId")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("权限 ID。");
|
||||
|
||||
b.Property<long>("RoleId")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("角色 ID。");
|
||||
|
||||
b.Property<long>("TenantId")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("所属租户 ID。");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("最近一次更新时间(UTC),从未更新时为 null。");
|
||||
|
||||
b.Property<long?>("UpdatedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("最后更新人用户标识,匿名或系统操作时为 null。");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
|
||||
b.HasIndex("TenantId", "RoleId", "PermissionId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("role_permissions", null, t =>
|
||||
{
|
||||
t.HasComment("角色-权限关系。");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TakeoutSaaS.Domain.Identity.Entities.RoleTemplate", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("实体唯一标识。");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("创建时间(UTC)。");
|
||||
|
||||
b.Property<long?>("CreatedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("创建人用户标识,匿名或系统操作时为 null。");
|
||||
|
||||
b.Property<DateTime?>("DeletedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("软删除时间(UTC),未删除时为 null。");
|
||||
|
||||
b.Property<long?>("DeletedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("删除人用户标识(软删除),未删除时为 null。");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)")
|
||||
.HasComment("模板描述。");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("boolean")
|
||||
.HasComment("是否启用。");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)")
|
||||
.HasComment("模板名称。");
|
||||
|
||||
b.Property<string>("TemplateCode")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)")
|
||||
.HasComment("模板编码(唯一)。");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("最近一次更新时间(UTC),从未更新时为 null。");
|
||||
|
||||
b.Property<long?>("UpdatedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("最后更新人用户标识,匿名或系统操作时为 null。");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TemplateCode")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("role_templates", null, t =>
|
||||
{
|
||||
t.HasComment("角色模板定义(平台级)。");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TakeoutSaaS.Domain.Identity.Entities.RoleTemplatePermission", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("实体唯一标识。");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("创建时间(UTC)。");
|
||||
|
||||
b.Property<long?>("CreatedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("创建人用户标识,匿名或系统操作时为 null。");
|
||||
|
||||
b.Property<DateTime?>("DeletedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("软删除时间(UTC),未删除时为 null。");
|
||||
|
||||
b.Property<long?>("DeletedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("删除人用户标识(软删除),未删除时为 null。");
|
||||
|
||||
b.Property<string>("PermissionCode")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)")
|
||||
.HasComment("权限编码。");
|
||||
|
||||
b.Property<long>("RoleTemplateId")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("模板 ID。");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("最近一次更新时间(UTC),从未更新时为 null。");
|
||||
|
||||
b.Property<long?>("UpdatedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("最后更新人用户标识,匿名或系统操作时为 null。");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleTemplateId", "PermissionCode")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("role_template_permissions", null, t =>
|
||||
{
|
||||
t.HasComment("角色模板-权限关系(平台级)。");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TakeoutSaaS.Domain.Identity.Entities.UserRole", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("实体唯一标识。");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("创建时间(UTC)。");
|
||||
|
||||
b.Property<long?>("CreatedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("创建人用户标识,匿名或系统操作时为 null。");
|
||||
|
||||
b.Property<DateTime?>("DeletedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("软删除时间(UTC),未删除时为 null。");
|
||||
|
||||
b.Property<long?>("DeletedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("删除人用户标识(软删除),未删除时为 null。");
|
||||
|
||||
b.Property<long>("RoleId")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("角色 ID。");
|
||||
|
||||
b.Property<long>("TenantId")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("所属租户 ID。");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("最近一次更新时间(UTC),从未更新时为 null。");
|
||||
|
||||
b.Property<long?>("UpdatedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("最后更新人用户标识,匿名或系统操作时为 null。");
|
||||
|
||||
b.Property<long>("UserId")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("用户 ID。");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
|
||||
b.HasIndex("TenantId", "UserId", "RoleId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("user_roles", null, t =>
|
||||
{
|
||||
t.HasComment("用户-角色关系。");
|
||||
});
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace TakeoutSaaS.Infrastructure.Migrations.IdentityDb
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddIdentityOutbox : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "OutboxMessage",
|
||||
columns: table => new
|
||||
{
|
||||
SequenceNumber = table.Column<long>(type: "bigint", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
EnqueueTime = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
|
||||
SentTime = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
Headers = table.Column<string>(type: "text", nullable: true),
|
||||
Properties = table.Column<string>(type: "text", nullable: true),
|
||||
InboxMessageId = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
InboxConsumerId = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
OutboxId = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
MessageId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
ContentType = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
||||
MessageType = table.Column<string>(type: "text", nullable: false),
|
||||
Body = table.Column<string>(type: "text", nullable: false),
|
||||
ConversationId = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
CorrelationId = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
InitiatorId = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
RequestId = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
SourceAddress = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||
DestinationAddress = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||
ResponseAddress = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||
FaultAddress = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||
ExpirationTime = table.Column<DateTime>(type: "timestamp with time zone", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_OutboxMessage", x => x.SequenceNumber);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "OutboxState",
|
||||
columns: table => new
|
||||
{
|
||||
OutboxId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
LockId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
RowVersion = table.Column<byte[]>(type: "bytea", rowVersion: true, nullable: true),
|
||||
Created = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
Delivered = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
|
||||
LastSequenceNumber = table.Column<long>(type: "bigint", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_OutboxState", x => x.OutboxId);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_OutboxMessage_EnqueueTime",
|
||||
table: "OutboxMessage",
|
||||
column: "EnqueueTime");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_OutboxMessage_ExpirationTime",
|
||||
table: "OutboxMessage",
|
||||
column: "ExpirationTime");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_OutboxMessage_InboxMessageId_InboxConsumerId_SequenceNumber",
|
||||
table: "OutboxMessage",
|
||||
columns: new[] { "InboxMessageId", "InboxConsumerId", "SequenceNumber" },
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_OutboxMessage_OutboxId_SequenceNumber",
|
||||
table: "OutboxMessage",
|
||||
columns: new[] { "OutboxId", "SequenceNumber" },
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_OutboxState_Created",
|
||||
table: "OutboxState",
|
||||
column: "Created");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "OutboxMessage");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "OutboxState");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,127 @@ namespace TakeoutSaaS.Infrastructure.Migrations.IdentityDb
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("MassTransit.EntityFrameworkCoreIntegration.OutboxMessage", b =>
|
||||
{
|
||||
b.Property<long>("SequenceNumber")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("SequenceNumber"));
|
||||
|
||||
b.Property<string>("Body")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ContentType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<Guid?>("ConversationId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid?>("CorrelationId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("DestinationAddress")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<DateTime?>("EnqueueTime")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime?>("ExpirationTime")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("FaultAddress")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<string>("Headers")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<Guid?>("InboxConsumerId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid?>("InboxMessageId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid?>("InitiatorId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("MessageId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("MessageType")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<Guid?>("OutboxId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Properties")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<Guid?>("RequestId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("ResponseAddress")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<DateTime>("SentTime")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("SourceAddress")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.HasKey("SequenceNumber");
|
||||
|
||||
b.HasIndex("EnqueueTime");
|
||||
|
||||
b.HasIndex("ExpirationTime");
|
||||
|
||||
b.HasIndex("OutboxId", "SequenceNumber")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("InboxMessageId", "InboxConsumerId", "SequenceNumber")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("OutboxMessage");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MassTransit.EntityFrameworkCoreIntegration.OutboxState", b =>
|
||||
{
|
||||
b.Property<Guid>("OutboxId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("Created")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime?>("Delivered")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<long?>("LastSequenceNumber")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<Guid>("LockId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<byte[]>("RowVersion")
|
||||
.IsConcurrencyToken()
|
||||
.ValueGeneratedOnAddOrUpdate()
|
||||
.HasColumnType("bytea");
|
||||
|
||||
b.HasKey("OutboxId");
|
||||
|
||||
b.HasIndex("Created");
|
||||
|
||||
b.ToTable("OutboxState");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TakeoutSaaS.Domain.Identity.Entities.IdentityUser", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
|
||||
@@ -0,0 +1,358 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
using TakeoutSaaS.Infrastructure.Logs.Persistence;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace TakeoutSaaS.Infrastructure.Migrations.LogsDb
|
||||
{
|
||||
[DbContext(typeof(TakeoutLogsDbContext))]
|
||||
[Migration("20251227004337_AddOperationLogInboxMessages")]
|
||||
partial class AddOperationLogInboxMessages
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "10.0.0")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("TakeoutSaaS.Domain.Membership.Entities.MemberGrowthLog", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("实体唯一标识。");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<int>("ChangeValue")
|
||||
.HasColumnType("integer")
|
||||
.HasComment("变动数量。");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("创建时间(UTC)。");
|
||||
|
||||
b.Property<long?>("CreatedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("创建人用户标识,匿名或系统操作时为 null。");
|
||||
|
||||
b.Property<int>("CurrentValue")
|
||||
.HasColumnType("integer")
|
||||
.HasComment("当前成长值。");
|
||||
|
||||
b.Property<DateTime?>("DeletedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("软删除时间(UTC),未删除时为 null。");
|
||||
|
||||
b.Property<long?>("DeletedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("删除人用户标识(软删除),未删除时为 null。");
|
||||
|
||||
b.Property<long>("MemberId")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("会员标识。");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)")
|
||||
.HasComment("备注。");
|
||||
|
||||
b.Property<DateTime>("OccurredAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("发生时间。");
|
||||
|
||||
b.Property<long>("TenantId")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("所属租户 ID。");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("最近一次更新时间(UTC),从未更新时为 null。");
|
||||
|
||||
b.Property<long?>("UpdatedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("最后更新人用户标识,匿名或系统操作时为 null。");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TenantId", "MemberId", "OccurredAt");
|
||||
|
||||
b.ToTable("member_growth_logs", null, t =>
|
||||
{
|
||||
t.HasComment("成长值变动日志。");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TakeoutSaaS.Domain.Merchants.Entities.MerchantAuditLog", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("实体唯一标识。");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<int>("Action")
|
||||
.HasColumnType("integer")
|
||||
.HasComment("动作类型。");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("创建时间(UTC)。");
|
||||
|
||||
b.Property<long?>("CreatedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("创建人用户标识,匿名或系统操作时为 null。");
|
||||
|
||||
b.Property<DateTime?>("DeletedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("软删除时间(UTC),未删除时为 null。");
|
||||
|
||||
b.Property<long?>("DeletedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("删除人用户标识(软删除),未删除时为 null。");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("character varying(1024)")
|
||||
.HasComment("详情描述。");
|
||||
|
||||
b.Property<long>("MerchantId")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("商户标识。");
|
||||
|
||||
b.Property<long?>("OperatorId")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("操作人 ID。");
|
||||
|
||||
b.Property<string>("OperatorName")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)")
|
||||
.HasComment("操作人名称。");
|
||||
|
||||
b.Property<long>("TenantId")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("所属租户 ID。");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)")
|
||||
.HasComment("标题。");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("最近一次更新时间(UTC),从未更新时为 null。");
|
||||
|
||||
b.Property<long?>("UpdatedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("最后更新人用户标识,匿名或系统操作时为 null。");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TenantId", "MerchantId");
|
||||
|
||||
b.ToTable("merchant_audit_logs", null, t =>
|
||||
{
|
||||
t.HasComment("商户入驻审核日志。");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TakeoutSaaS.Domain.Tenants.Entities.OperationLog", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("实体唯一标识。");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("创建时间(UTC)。");
|
||||
|
||||
b.Property<long?>("CreatedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("创建人用户标识,匿名或系统操作时为 null。");
|
||||
|
||||
b.Property<DateTime?>("DeletedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("软删除时间(UTC),未删除时为 null。");
|
||||
|
||||
b.Property<long?>("DeletedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("删除人用户标识(软删除),未删除时为 null。");
|
||||
|
||||
b.Property<string>("OperationType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)")
|
||||
.HasComment("操作类型:BatchExtend, BatchRemind, StatusChange 等。");
|
||||
|
||||
b.Property<string>("OperatorId")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)")
|
||||
.HasComment("操作人ID。");
|
||||
|
||||
b.Property<string>("OperatorName")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)")
|
||||
.HasComment("操作人名称。");
|
||||
|
||||
b.Property<string>("Parameters")
|
||||
.HasColumnType("text")
|
||||
.HasComment("操作参数(JSON)。");
|
||||
|
||||
b.Property<string>("Result")
|
||||
.HasColumnType("text")
|
||||
.HasComment("操作结果(JSON)。");
|
||||
|
||||
b.Property<bool>("Success")
|
||||
.HasColumnType("boolean")
|
||||
.HasComment("是否成功。");
|
||||
|
||||
b.Property<string>("TargetIds")
|
||||
.HasColumnType("text")
|
||||
.HasComment("目标ID列表(JSON)。");
|
||||
|
||||
b.Property<string>("TargetType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)")
|
||||
.HasComment("目标类型:Subscription, Bill 等。");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("最近一次更新时间(UTC),从未更新时为 null。");
|
||||
|
||||
b.Property<long?>("UpdatedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("最后更新人用户标识,匿名或系统操作时为 null。");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CreatedAt");
|
||||
|
||||
b.HasIndex("OperationType", "CreatedAt");
|
||||
|
||||
b.ToTable("operation_logs", null, t =>
|
||||
{
|
||||
t.HasComment("运营操作日志。");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TakeoutSaaS.Domain.Tenants.Entities.TenantAuditLog", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("实体唯一标识。");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<int>("Action")
|
||||
.HasColumnType("integer")
|
||||
.HasComment("操作类型。");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("创建时间(UTC)。");
|
||||
|
||||
b.Property<long?>("CreatedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("创建人用户标识,匿名或系统操作时为 null。");
|
||||
|
||||
b.Property<int?>("CurrentStatus")
|
||||
.HasColumnType("integer")
|
||||
.HasComment("新状态。");
|
||||
|
||||
b.Property<DateTime?>("DeletedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("软删除时间(UTC),未删除时为 null。");
|
||||
|
||||
b.Property<long?>("DeletedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("删除人用户标识(软删除),未删除时为 null。");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("character varying(1024)")
|
||||
.HasComment("详细描述。");
|
||||
|
||||
b.Property<long?>("OperatorId")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("操作人 ID。");
|
||||
|
||||
b.Property<string>("OperatorName")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)")
|
||||
.HasComment("操作人名称。");
|
||||
|
||||
b.Property<int?>("PreviousStatus")
|
||||
.HasColumnType("integer")
|
||||
.HasComment("原状态。");
|
||||
|
||||
b.Property<long>("TenantId")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("关联的租户标识。");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)")
|
||||
.HasComment("日志标题。");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasComment("最近一次更新时间(UTC),从未更新时为 null。");
|
||||
|
||||
b.Property<long?>("UpdatedBy")
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("最后更新人用户标识,匿名或系统操作时为 null。");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
|
||||
b.ToTable("tenant_audit_logs", null, t =>
|
||||
{
|
||||
t.HasComment("租户运营审核日志。");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TakeoutSaaS.Infrastructure.Logs.Persistence.OperationLogInboxMessage", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("实体唯一标识。");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("ConsumedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<Guid>("MessageId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MessageId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("operation_log_inbox_messages", (string)null);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace TakeoutSaaS.Infrastructure.Migrations.LogsDb
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddOperationLogInboxMessages : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "operation_log_inbox_messages",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<long>(type: "bigint", nullable: false, comment: "实体唯一标识。")
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
MessageId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
ConsumedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_operation_log_inbox_messages", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_operation_log_inbox_messages_MessageId",
|
||||
table: "operation_log_inbox_messages",
|
||||
column: "MessageId",
|
||||
unique: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "operation_log_inbox_messages");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -326,6 +326,29 @@ namespace TakeoutSaaS.Infrastructure.Migrations.LogsDb
|
||||
t.HasComment("租户运营审核日志。");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TakeoutSaaS.Infrastructure.Logs.Persistence.OperationLogInboxMessage", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasComment("实体唯一标识。");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("ConsumedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<Guid>("MessageId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MessageId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("operation_log_inbox_messages", (string)null);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user