feat(member): change MemberId to Guid strongly typed id
- Convert MemberId from long to Guid strongly typed ID - Update all Member commands to use record pattern with MemberId - Update all Member endpoints to use record pattern with MemberId - Update entity configurations to use GuidVersion7ValueGenerator - Add implicit conversion operators for MemberId Migration: ChangeMemberIdToGuid
This commit is contained in:
parent
3b8b86a258
commit
96503a593d
@ -5,64 +5,34 @@ using Fengling.Member.Infrastructure.Repositories;
|
|||||||
|
|
||||||
namespace Fengling.Member.Application.Commands.Member;
|
namespace Fengling.Member.Application.Commands.Member;
|
||||||
|
|
||||||
public class AddMemberTagCommand : IRequest<AddMemberTagResponse>
|
public record AddMemberTagCommand(MemberId MemberId, string TagId, string? TagName)
|
||||||
{
|
: IRequest<AddMemberTagResponse>;
|
||||||
public long MemberId { get; set; }
|
|
||||||
public string TagId { get; set; } = string.Empty;
|
|
||||||
public string? TagName { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AddMemberTagResponse
|
public record AddMemberTagResponse(MemberId MemberId, string TagId, string? TagName, DateTime AddedAt);
|
||||||
{
|
|
||||||
public long MemberId { get; set; }
|
|
||||||
public string TagId { get; set; } = string.Empty;
|
|
||||||
public string? TagName { get; set; }
|
|
||||||
public DateTime AddedAt { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AddMemberTagCommandValidator : AbstractValidator<AddMemberTagCommand>
|
public class AddMemberTagCommandValidator : AbstractValidator<AddMemberTagCommand>
|
||||||
{
|
{
|
||||||
public AddMemberTagCommandValidator()
|
public AddMemberTagCommandValidator()
|
||||||
{
|
{
|
||||||
RuleFor(x => x.MemberId).GreaterThan(0);
|
|
||||||
RuleFor(x => x.TagId).NotEmpty().MaximumLength(50);
|
RuleFor(x => x.TagId).NotEmpty().MaximumLength(50);
|
||||||
RuleFor(x => x.TagName).MaximumLength(100);
|
RuleFor(x => x.TagName).MaximumLength(100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AddMemberTagCommandHandler : IRequestHandler<AddMemberTagCommand, AddMemberTagResponse>
|
public class AddMemberTagCommandHandler(IMemberRepository memberRepository, ILogger<AddMemberTagCommandHandler> logger)
|
||||||
|
: IRequestHandler<AddMemberTagCommand, AddMemberTagResponse>
|
||||||
{
|
{
|
||||||
private readonly IMemberRepository _memberRepository;
|
|
||||||
private readonly ILogger<AddMemberTagCommandHandler> _logger;
|
|
||||||
|
|
||||||
public AddMemberTagCommandHandler(
|
|
||||||
IMemberRepository memberRepository,
|
|
||||||
ILogger<AddMemberTagCommandHandler> logger)
|
|
||||||
{
|
|
||||||
_memberRepository = memberRepository;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<AddMemberTagResponse> Handle(AddMemberTagCommand request, CancellationToken cancellationToken)
|
public async Task<AddMemberTagResponse> Handle(AddMemberTagCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Adding tag {TagId} to member {MemberId}", request.TagId, request.MemberId);
|
logger.LogInformation("Adding tag {TagId} to member {MemberId}", request.TagId, request.MemberId);
|
||||||
|
|
||||||
var member = await _memberRepository.GetAsync(request.MemberId, cancellationToken);
|
var member = await memberRepository.GetAsync(request.MemberId, cancellationToken)
|
||||||
if (member == null)
|
?? throw new KeyNotFoundException($"会员不存在: {request.MemberId}");
|
||||||
{
|
|
||||||
throw new KeyNotFoundException($"会员不存在: {request.MemberId}");
|
|
||||||
}
|
|
||||||
|
|
||||||
member.AddTag(request.TagId, request.TagName);
|
member.AddTag(request.TagId, request.TagName);
|
||||||
|
|
||||||
_logger.LogInformation("Tag {TagId} added to member {MemberId}", request.TagId, request.MemberId);
|
logger.LogInformation("Tag {TagId} added to member {MemberId}", request.TagId, request.MemberId);
|
||||||
|
|
||||||
return new AddMemberTagResponse
|
return new AddMemberTagResponse(member.Id, request.TagId, request.TagName, DateTime.UtcNow);
|
||||||
{
|
|
||||||
MemberId = member.Id,
|
|
||||||
TagId = request.TagId,
|
|
||||||
TagName = request.TagName,
|
|
||||||
AddedAt = DateTime.UtcNow
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,64 +5,34 @@ using Fengling.Member.Infrastructure.Repositories;
|
|||||||
|
|
||||||
namespace Fengling.Member.Application.Commands.Member;
|
namespace Fengling.Member.Application.Commands.Member;
|
||||||
|
|
||||||
public class BindAlipayCommand : IRequest<BindAlipayResponse>
|
public record BindAlipayCommand(MemberId MemberId, string AlipayOpenId, string? AlipayUserId)
|
||||||
{
|
: IRequest<BindAlipayResponse>;
|
||||||
public long MemberId { get; set; }
|
|
||||||
public string AlipayOpenId { get; set; } = string.Empty;
|
|
||||||
public string? AlipayUserId { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class BindAlipayResponse
|
public record BindAlipayResponse(MemberId MemberId, string AlipayOpenId, string? AlipayUserId, DateTime BoundAt);
|
||||||
{
|
|
||||||
public long MemberId { get; set; }
|
|
||||||
public string AlipayOpenId { get; set; } = string.Empty;
|
|
||||||
public string? AlipayUserId { get; set; }
|
|
||||||
public DateTime BoundAt { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class BindAlipayCommandValidator : AbstractValidator<BindAlipayCommand>
|
public class BindAlipayCommandValidator : AbstractValidator<BindAlipayCommand>
|
||||||
{
|
{
|
||||||
public BindAlipayCommandValidator()
|
public BindAlipayCommandValidator()
|
||||||
{
|
{
|
||||||
RuleFor(x => x.MemberId).GreaterThan(0);
|
|
||||||
RuleFor(x => x.AlipayOpenId).NotEmpty().MaximumLength(128);
|
RuleFor(x => x.AlipayOpenId).NotEmpty().MaximumLength(128);
|
||||||
RuleFor(x => x.AlipayUserId).MaximumLength(128);
|
RuleFor(x => x.AlipayUserId).MaximumLength(128);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BindAlipayCommandHandler : IRequestHandler<BindAlipayCommand, BindAlipayResponse>
|
public class BindAlipayCommandHandler(IMemberRepository memberRepository, ILogger<BindAlipayCommandHandler> logger)
|
||||||
|
: IRequestHandler<BindAlipayCommand, BindAlipayResponse>
|
||||||
{
|
{
|
||||||
private readonly IMemberRepository _memberRepository;
|
|
||||||
private readonly ILogger<BindAlipayCommandHandler> _logger;
|
|
||||||
|
|
||||||
public BindAlipayCommandHandler(
|
|
||||||
IMemberRepository memberRepository,
|
|
||||||
ILogger<BindAlipayCommandHandler> logger)
|
|
||||||
{
|
|
||||||
_memberRepository = memberRepository;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<BindAlipayResponse> Handle(BindAlipayCommand request, CancellationToken cancellationToken)
|
public async Task<BindAlipayResponse> Handle(BindAlipayCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Binding Alipay for member {MemberId}", request.MemberId);
|
logger.LogInformation("Binding Alipay for member {MemberId}", request.MemberId);
|
||||||
|
|
||||||
var member = await _memberRepository.GetAsync(request.MemberId, cancellationToken);
|
var member = await memberRepository.GetAsync(request.MemberId, cancellationToken)
|
||||||
if (member == null)
|
?? throw new KeyNotFoundException($"会员不存在: {request.MemberId}");
|
||||||
{
|
|
||||||
throw new KeyNotFoundException($"会员不存在: {request.MemberId}");
|
|
||||||
}
|
|
||||||
|
|
||||||
member.BindOAuth(OAuthProvider.Alipay, request.AlipayOpenId, request.AlipayUserId);
|
member.BindOAuth(OAuthProvider.Alipay, request.AlipayOpenId, request.AlipayUserId);
|
||||||
|
|
||||||
_logger.LogInformation("Alipay bound successfully for member {MemberId}", request.MemberId);
|
logger.LogInformation("Alipay bound successfully for member {MemberId}", request.MemberId);
|
||||||
|
|
||||||
return new BindAlipayResponse
|
return new BindAlipayResponse(member.Id, request.AlipayOpenId, request.AlipayUserId, DateTime.UtcNow);
|
||||||
{
|
|
||||||
MemberId = member.Id,
|
|
||||||
AlipayOpenId = request.AlipayOpenId,
|
|
||||||
AlipayUserId = request.AlipayUserId,
|
|
||||||
BoundAt = DateTime.UtcNow
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,70 +5,37 @@ using Fengling.Member.Infrastructure.Repositories;
|
|||||||
|
|
||||||
namespace Fengling.Member.Application.Commands.Member;
|
namespace Fengling.Member.Application.Commands.Member;
|
||||||
|
|
||||||
public class BindOAuthCommand : IRequest<BindOAuthResponse>
|
public record BindOAuthCommand(OAuthProvider Provider, MemberId MemberId, string OpenId, string? UnionId)
|
||||||
{
|
: IRequest<BindOAuthResponse>;
|
||||||
public OAuthProvider Provider { get; set; }
|
|
||||||
public long MemberId { get; set; }
|
|
||||||
public string OpenId { get; set; } = string.Empty;
|
|
||||||
public string? UnionId { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class BindOAuthResponse
|
public record BindOAuthResponse(MemberId MemberId, OAuthProvider Provider, string OpenId, string? UnionId, DateTime BoundAt);
|
||||||
{
|
|
||||||
public long MemberId { get; set; }
|
|
||||||
public OAuthProvider Provider { get; set; }
|
|
||||||
public string OpenId { get; set; } = string.Empty;
|
|
||||||
public string? UnionId { get; set; }
|
|
||||||
public DateTime BoundAt { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class BindOAuthCommandValidator : AbstractValidator<BindOAuthCommand>
|
public class BindOAuthCommandValidator : AbstractValidator<BindOAuthCommand>
|
||||||
{
|
{
|
||||||
public BindOAuthCommandValidator()
|
public BindOAuthCommandValidator()
|
||||||
{
|
{
|
||||||
RuleFor(x => x.Provider).IsInEnum();
|
RuleFor(x => x.Provider).IsInEnum();
|
||||||
RuleFor(x => x.MemberId).GreaterThan(0);
|
|
||||||
RuleFor(x => x.OpenId).NotEmpty().MaximumLength(128);
|
RuleFor(x => x.OpenId).NotEmpty().MaximumLength(128);
|
||||||
RuleFor(x => x.UnionId).MaximumLength(128);
|
RuleFor(x => x.UnionId).MaximumLength(128);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BindOAuthCommandHandler : IRequestHandler<BindOAuthCommand, BindOAuthResponse>
|
public class BindOAuthCommandHandler(IMemberRepository memberRepository, ILogger<BindOAuthCommandHandler> logger)
|
||||||
|
: IRequestHandler<BindOAuthCommand, BindOAuthResponse>
|
||||||
{
|
{
|
||||||
private readonly IMemberRepository _memberRepository;
|
|
||||||
private readonly ILogger<BindOAuthCommandHandler> _logger;
|
|
||||||
|
|
||||||
public BindOAuthCommandHandler(
|
|
||||||
IMemberRepository memberRepository,
|
|
||||||
ILogger<BindOAuthCommandHandler> logger)
|
|
||||||
{
|
|
||||||
_memberRepository = memberRepository;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<BindOAuthResponse> Handle(BindOAuthCommand request, CancellationToken cancellationToken)
|
public async Task<BindOAuthResponse> Handle(BindOAuthCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Binding {Provider} for member {MemberId}",
|
logger.LogInformation("Binding {Provider} for member {MemberId}",
|
||||||
request.Provider.GetProviderName(), request.MemberId);
|
request.Provider.GetProviderName(), request.MemberId);
|
||||||
|
|
||||||
var member = await _memberRepository.GetAsync(request.MemberId, cancellationToken);
|
var member = await memberRepository.GetAsync(request.MemberId, cancellationToken)
|
||||||
if (member == null)
|
?? throw new KeyNotFoundException($"会员不存在: {request.MemberId}");
|
||||||
{
|
|
||||||
throw new KeyNotFoundException($"会员不存在: {request.MemberId}");
|
|
||||||
}
|
|
||||||
|
|
||||||
member.BindOAuth(request.Provider, request.OpenId, request.UnionId);
|
member.BindOAuth(request.Provider, request.OpenId, request.UnionId);
|
||||||
|
|
||||||
_logger.LogInformation("{Provider} bound successfully for member {MemberId}",
|
logger.LogInformation("{Provider} bound successfully for member {MemberId}",
|
||||||
request.Provider.GetProviderName(), request.MemberId);
|
request.Provider.GetProviderName(), request.MemberId);
|
||||||
|
|
||||||
return new BindOAuthResponse
|
return new BindOAuthResponse(member.Id, request.Provider, request.OpenId, request.UnionId, DateTime.UtcNow);
|
||||||
{
|
|
||||||
MemberId = member.Id,
|
|
||||||
Provider = request.Provider,
|
|
||||||
OpenId = request.OpenId,
|
|
||||||
UnionId = request.UnionId,
|
|
||||||
BoundAt = DateTime.UtcNow
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,64 +5,34 @@ using Fengling.Member.Infrastructure.Repositories;
|
|||||||
|
|
||||||
namespace Fengling.Member.Application.Commands.Member;
|
namespace Fengling.Member.Application.Commands.Member;
|
||||||
|
|
||||||
public class BindWechatCommand : IRequest<BindWechatResponse>
|
public record BindWechatCommand(MemberId MemberId, string OpenId, string? UnionId)
|
||||||
{
|
: IRequest<BindWechatResponse>;
|
||||||
public long MemberId { get; set; }
|
|
||||||
public string OpenId { get; set; } = string.Empty;
|
|
||||||
public string? UnionId { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class BindWechatResponse
|
public record BindWechatResponse(MemberId MemberId, string OpenId, string? UnionId, DateTime BoundAt);
|
||||||
{
|
|
||||||
public long MemberId { get; set; }
|
|
||||||
public string OpenId { get; set; } = string.Empty;
|
|
||||||
public string? UnionId { get; set; }
|
|
||||||
public DateTime BoundAt { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class BindWechatCommandValidator : AbstractValidator<BindWechatCommand>
|
public class BindWechatCommandValidator : AbstractValidator<BindWechatCommand>
|
||||||
{
|
{
|
||||||
public BindWechatCommandValidator()
|
public BindWechatCommandValidator()
|
||||||
{
|
{
|
||||||
RuleFor(x => x.MemberId).GreaterThan(0);
|
|
||||||
RuleFor(x => x.OpenId).NotEmpty().MaximumLength(64);
|
RuleFor(x => x.OpenId).NotEmpty().MaximumLength(64);
|
||||||
RuleFor(x => x.UnionId).MaximumLength(64);
|
RuleFor(x => x.UnionId).MaximumLength(64);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BindWechatCommandHandler : IRequestHandler<BindWechatCommand, BindWechatResponse>
|
public class BindWechatCommandHandler(IMemberRepository memberRepository, ILogger<BindWechatCommandHandler> logger)
|
||||||
|
: IRequestHandler<BindWechatCommand, BindWechatResponse>
|
||||||
{
|
{
|
||||||
private readonly IMemberRepository _memberRepository;
|
|
||||||
private readonly ILogger<BindWechatCommandHandler> _logger;
|
|
||||||
|
|
||||||
public BindWechatCommandHandler(
|
|
||||||
IMemberRepository memberRepository,
|
|
||||||
ILogger<BindWechatCommandHandler> logger)
|
|
||||||
{
|
|
||||||
_memberRepository = memberRepository;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<BindWechatResponse> Handle(BindWechatCommand request, CancellationToken cancellationToken)
|
public async Task<BindWechatResponse> Handle(BindWechatCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Binding wechat for member {MemberId}", request.MemberId);
|
logger.LogInformation("Binding wechat for member {MemberId}", request.MemberId);
|
||||||
|
|
||||||
var member = await _memberRepository.GetAsync(request.MemberId, cancellationToken);
|
var member = await memberRepository.GetAsync(request.MemberId, cancellationToken)
|
||||||
if (member == null)
|
?? throw new KeyNotFoundException($"会员不存在: {request.MemberId}");
|
||||||
{
|
|
||||||
throw new KeyNotFoundException($"会员不存在: {request.MemberId}");
|
|
||||||
}
|
|
||||||
|
|
||||||
member.BindWechat(request.OpenId, request.UnionId);
|
member.BindWechat(request.OpenId, request.UnionId);
|
||||||
|
|
||||||
_logger.LogInformation("Wechat bound successfully for member {MemberId}", request.MemberId);
|
logger.LogInformation("Wechat bound successfully for member {MemberId}", request.MemberId);
|
||||||
|
|
||||||
return new BindWechatResponse
|
return new BindWechatResponse(member.Id, request.OpenId, request.UnionId, DateTime.UtcNow);
|
||||||
{
|
|
||||||
MemberId = member.Id,
|
|
||||||
OpenId = request.OpenId,
|
|
||||||
UnionId = request.UnionId,
|
|
||||||
BoundAt = DateTime.UtcNow
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,48 +5,33 @@ using Fengling.Member.Infrastructure.Repositories;
|
|||||||
|
|
||||||
namespace Fengling.Member.Application.Commands.Member;
|
namespace Fengling.Member.Application.Commands.Member;
|
||||||
|
|
||||||
public class RegisterMemberCommand : IRequest<RegisterMemberResponse>
|
public record RegisterMemberCommand(long TenantId, string? PhoneNumber, string? OpenId, string? UnionId, string? Source)
|
||||||
|
: IRequest<RegisterMemberResponse>;
|
||||||
|
|
||||||
|
public record RegisterMemberResponse(MemberId MemberId, long TenantId, string? PhoneNumber, string? OpenId, MemberStatus Status, DateTime RegisteredAt);
|
||||||
|
|
||||||
|
public class RegisterMemberCommandValidator : AbstractValidator<RegisterMemberCommand>
|
||||||
{
|
{
|
||||||
public long TenantId { get; set; }
|
public RegisterMemberCommandValidator()
|
||||||
public string? PhoneNumber { get; set; }
|
{
|
||||||
public string? OpenId { get; set; }
|
RuleFor(x => x.TenantId).GreaterThan(0);
|
||||||
public string? UnionId { get; set; }
|
RuleFor(x => x.PhoneNumber).Matches(@"^1[3-9]\d{9}$").When(x => x.PhoneNumber != null);
|
||||||
public string? Source { get; set; }
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class RegisterMemberResponse
|
public class RegisterMemberCommandHandler(IMemberRepository memberRepository, ILogger<RegisterMemberCommandHandler> logger)
|
||||||
|
: IRequestHandler<RegisterMemberCommand, RegisterMemberResponse>
|
||||||
{
|
{
|
||||||
public long MemberId { get; set; }
|
|
||||||
public long TenantId { get; set; }
|
|
||||||
public string? PhoneNumber { get; set; }
|
|
||||||
public string? OpenId { get; set; }
|
|
||||||
public MemberStatus Status { get; set; } = MemberStatus.Active;
|
|
||||||
public DateTime RegisteredAt { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class RegisterMemberCommandHandler : IRequestHandler<RegisterMemberCommand, RegisterMemberResponse>
|
|
||||||
{
|
|
||||||
private readonly IMemberRepository _memberRepository;
|
|
||||||
private readonly ILogger<RegisterMemberCommandHandler> _logger;
|
|
||||||
|
|
||||||
public RegisterMemberCommandHandler(
|
|
||||||
IMemberRepository memberRepository,
|
|
||||||
ILogger<RegisterMemberCommandHandler> logger)
|
|
||||||
{
|
|
||||||
_memberRepository = memberRepository;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<RegisterMemberResponse> Handle(RegisterMemberCommand request, CancellationToken cancellationToken)
|
public async Task<RegisterMemberResponse> Handle(RegisterMemberCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Registering new member for tenant {TenantId}", request.TenantId);
|
logger.LogInformation("Registering new member for tenant {TenantId}", request.TenantId);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(request.PhoneNumber))
|
if (!string.IsNullOrEmpty(request.PhoneNumber))
|
||||||
{
|
{
|
||||||
var existingMember = await _memberRepository.GetByPhoneNumberAsync(request.TenantId, request.PhoneNumber, cancellationToken);
|
var existingMember = await memberRepository.GetByPhoneNumberAsync(request.TenantId, request.PhoneNumber, cancellationToken);
|
||||||
if (existingMember != null)
|
if (existingMember != null)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Member with phone {PhoneNumber} already exists in tenant {TenantId}",
|
logger.LogWarning("Member with phone {PhoneNumber} already exists in tenant {TenantId}",
|
||||||
request.PhoneNumber, request.TenantId);
|
request.PhoneNumber, request.TenantId);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(request.OpenId) && string.IsNullOrEmpty(existingMember.OpenId))
|
if (!string.IsNullOrEmpty(request.OpenId) && string.IsNullOrEmpty(existingMember.OpenId))
|
||||||
@ -54,34 +39,18 @@ public class RegisterMemberCommandHandler : IRequestHandler<RegisterMemberComman
|
|||||||
existingMember.BindWechat(request.OpenId, request.UnionId);
|
existingMember.BindWechat(request.OpenId, request.UnionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new RegisterMemberResponse
|
return new RegisterMemberResponse(existingMember.Id, existingMember.TenantId, existingMember.PhoneNumber, existingMember.OpenId, existingMember.Status, existingMember.CreatedAt);
|
||||||
{
|
|
||||||
MemberId = existingMember.Id,
|
|
||||||
TenantId = existingMember.TenantId,
|
|
||||||
PhoneNumber = existingMember.PhoneNumber,
|
|
||||||
OpenId = existingMember.OpenId,
|
|
||||||
Status = existingMember.Status,
|
|
||||||
RegisteredAt = existingMember.CreatedAt
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(request.OpenId))
|
if (!string.IsNullOrEmpty(request.OpenId))
|
||||||
{
|
{
|
||||||
var existingByOpenId = await _memberRepository.GetByOpenIdAsync(request.OpenId, cancellationToken);
|
var existingByOpenId = await memberRepository.GetByOpenIdAsync(request.OpenId, cancellationToken);
|
||||||
if (existingByOpenId != null)
|
if (existingByOpenId != null)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Member with OpenId {OpenId} already exists", request.OpenId);
|
logger.LogWarning("Member with OpenId {OpenId} already exists", request.OpenId);
|
||||||
|
|
||||||
return new RegisterMemberResponse
|
return new RegisterMemberResponse(existingByOpenId.Id, existingByOpenId.TenantId, existingByOpenId.PhoneNumber, existingByOpenId.OpenId, existingByOpenId.Status, existingByOpenId.CreatedAt);
|
||||||
{
|
|
||||||
MemberId = existingByOpenId.Id,
|
|
||||||
TenantId = existingByOpenId.TenantId,
|
|
||||||
PhoneNumber = existingByOpenId.PhoneNumber,
|
|
||||||
OpenId = existingByOpenId.OpenId,
|
|
||||||
Status = existingByOpenId.Status,
|
|
||||||
RegisteredAt = existingByOpenId.CreatedAt
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,18 +61,10 @@ public class RegisterMemberCommandHandler : IRequestHandler<RegisterMemberComman
|
|||||||
member.BindWechat(request.OpenId, request.UnionId);
|
member.BindWechat(request.OpenId, request.UnionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
await _memberRepository.AddAsync(member, cancellationToken);
|
await memberRepository.AddAsync(member, cancellationToken);
|
||||||
|
|
||||||
_logger.LogInformation("Member registered successfully with ID {MemberId}", member.Id);
|
logger.LogInformation("Member registered successfully with ID {MemberId}", member.Id);
|
||||||
|
|
||||||
return new RegisterMemberResponse
|
return new RegisterMemberResponse(member.Id, member.TenantId, member.PhoneNumber, member.OpenId, member.Status, member.CreatedAt);
|
||||||
{
|
|
||||||
MemberId = member.Id,
|
|
||||||
TenantId = member.TenantId,
|
|
||||||
PhoneNumber = member.PhoneNumber,
|
|
||||||
OpenId = member.OpenId,
|
|
||||||
Status = member.Status,
|
|
||||||
RegisteredAt = member.CreatedAt
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,17 @@ using Fengling.Member.Domain.Events.Member;
|
|||||||
|
|
||||||
namespace Fengling.Member.Domain.Aggregates.Users;
|
namespace Fengling.Member.Domain.Aggregates.Users;
|
||||||
|
|
||||||
public class MemberEntity : Entity<long>, IAggregateRoot
|
public partial record MemberId : IGuidStronglyTypedId
|
||||||
|
{
|
||||||
|
public static MemberId New() => new MemberId(Guid.NewGuid());
|
||||||
|
|
||||||
|
public Guid Value => this;
|
||||||
|
|
||||||
|
public static implicit operator Guid(MemberId id) => id.Value;
|
||||||
|
public static implicit operator MemberId(Guid value) => new MemberId(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MemberEntity : Entity<MemberId>, IAggregateRoot
|
||||||
{
|
{
|
||||||
public long TenantId { get; private set; }
|
public long TenantId { get; private set; }
|
||||||
public string? PhoneNumber { get; private set; }
|
public string? PhoneNumber { get; private set; }
|
||||||
|
|||||||
@ -2,7 +2,7 @@ namespace Fengling.Member.Domain.Aggregates.Users;
|
|||||||
|
|
||||||
public class MemberTag : Entity<long>
|
public class MemberTag : Entity<long>
|
||||||
{
|
{
|
||||||
public long MemberId { get; private set; }
|
public MemberId MemberId { get; private set; } = default!;
|
||||||
public string TagId { get; private set; } = string.Empty;
|
public string TagId { get; private set; } = string.Empty;
|
||||||
public string? TagName { get; private set; }
|
public string? TagName { get; private set; }
|
||||||
public DateTime CreatedAt { get; private set; } = DateTime.UtcNow;
|
public DateTime CreatedAt { get; private set; } = DateTime.UtcNow;
|
||||||
@ -11,7 +11,7 @@ public class MemberTag : Entity<long>
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MemberTag Create(long memberId, string tagId, string? tagName = null)
|
public static MemberTag Create(MemberId memberId, string tagId, string? tagName = null)
|
||||||
{
|
{
|
||||||
return new MemberTag
|
return new MemberTag
|
||||||
{
|
{
|
||||||
|
|||||||
@ -2,7 +2,7 @@ namespace Fengling.Member.Domain.Aggregates.Users;
|
|||||||
|
|
||||||
public class OAuthAuthorization : Entity<long>
|
public class OAuthAuthorization : Entity<long>
|
||||||
{
|
{
|
||||||
public long MemberId { get; private set; }
|
public MemberId MemberId { get; private set; } = default!;
|
||||||
public OAuthProvider Provider { get; private set; }
|
public OAuthProvider Provider { get; private set; }
|
||||||
public string OpenId { get; private set; } = string.Empty;
|
public string OpenId { get; private set; } = string.Empty;
|
||||||
public string? UnionId { get; private set; }
|
public string? UnionId { get; private set; }
|
||||||
@ -16,7 +16,7 @@ public class OAuthAuthorization : Entity<long>
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public static OAuthAuthorization Create(long memberId, OAuthProvider provider, string openId, string? unionId = null)
|
public static OAuthAuthorization Create(MemberId memberId, OAuthProvider provider, string openId, string? unionId = null)
|
||||||
{
|
{
|
||||||
return new OAuthAuthorization
|
return new OAuthAuthorization
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
|
using Fengling.Member.Domain.Aggregates.Users;
|
||||||
|
|
||||||
namespace Fengling.Member.Domain.Events.Member;
|
namespace Fengling.Member.Domain.Events.Member;
|
||||||
|
|
||||||
public record MemberRegisteredEvent(
|
public record MemberRegisteredEvent(
|
||||||
long MemberId,
|
MemberId MemberId,
|
||||||
long TenantId,
|
long TenantId,
|
||||||
string? PhoneNumber,
|
string? PhoneNumber,
|
||||||
DateTime RegisteredAt
|
DateTime RegisteredAt
|
||||||
|
|||||||
@ -19,7 +19,8 @@ public class MemberEntityTypeConfiguration : IEntityTypeConfiguration<MemberEnti
|
|||||||
|
|
||||||
builder.Property(m => m.Id)
|
builder.Property(m => m.Id)
|
||||||
.HasColumnName("id")
|
.HasColumnName("id")
|
||||||
.UseIdentityColumn();
|
.UseGuidVersion7ValueGenerator()
|
||||||
|
.HasComment("会员标识");
|
||||||
|
|
||||||
builder.Property(m => m.TenantId)
|
builder.Property(m => m.TenantId)
|
||||||
.HasColumnName("tenant_id")
|
.HasColumnName("tenant_id")
|
||||||
|
|||||||
596
src/Fengling.Member.Infrastructure/Migrations/20260209163416_ChangeMemberIdToGuid.Designer.cs
generated
Normal file
596
src/Fengling.Member.Infrastructure/Migrations/20260209163416_ChangeMemberIdToGuid.Designer.cs
generated
Normal file
@ -0,0 +1,596 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Fengling.Member.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Fengling.Member.Infrastructure.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(ApplicationDbContext))]
|
||||||
|
[Migration("20260209163416_ChangeMemberIdToGuid")]
|
||||||
|
partial class ChangeMemberIdToGuid
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "9.0.0")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.PointsModel.PointsAccount", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created_at");
|
||||||
|
|
||||||
|
b.Property<int>("FrozenPoints")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasDefaultValue(0)
|
||||||
|
.HasColumnName("frozen_points");
|
||||||
|
|
||||||
|
b.Property<long>("MemberId")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("user_id");
|
||||||
|
|
||||||
|
b.Property<long>("TenantId")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("tenant_id");
|
||||||
|
|
||||||
|
b.Property<int>("TotalPoints")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasDefaultValue(0)
|
||||||
|
.HasColumnName("points");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("updated_at");
|
||||||
|
|
||||||
|
b.Property<int>("Version")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasDefaultValue(1)
|
||||||
|
.HasColumnName("version");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("MemberId")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx_points_account_memberid");
|
||||||
|
|
||||||
|
b.HasIndex("MemberId", "TenantId")
|
||||||
|
.HasDatabaseName("idx_points_account_member_tenant");
|
||||||
|
|
||||||
|
b.ToTable("mka_integraldetails", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.PointsModel.PointsTransaction", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ExpireAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<long>("MemberId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<int>("Points")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<long>("PointsAccountId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<string>("Remark")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("SourceId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("TransactionType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<int>("TransactionTypeCategory")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("PointsAccountId");
|
||||||
|
|
||||||
|
b.ToTable("PointsTransactions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.PointsRuleModel.PointsRule", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasComment("规则标识");
|
||||||
|
|
||||||
|
b.Property<int>("BasePoints")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasComment("基础积分");
|
||||||
|
|
||||||
|
b.Property<int>("CalculationMode")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasComment("计算模式");
|
||||||
|
|
||||||
|
b.Property<string>("Code")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("character varying(50)")
|
||||||
|
.HasComment("规则编码");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<DateTime>("EffectiveFrom")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasComment("生效开始时间");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("EffectiveTo")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasComment("生效结束时间");
|
||||||
|
|
||||||
|
b.Property<bool>("IsActive")
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasComment("是否启用");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)")
|
||||||
|
.HasComment("规则名称");
|
||||||
|
|
||||||
|
b.Property<int>("Priority")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasComment("优先级");
|
||||||
|
|
||||||
|
b.Property<int>("RuleType")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasComment("规则类型");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasComment("更新时间");
|
||||||
|
|
||||||
|
b.Property<int>("ValidityDays")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasComment("有效期天数");
|
||||||
|
|
||||||
|
b.Property<decimal?>("WeightFactor")
|
||||||
|
.HasPrecision(18, 4)
|
||||||
|
.HasColumnType("numeric(18,4)")
|
||||||
|
.HasComment("权重因子");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Code")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.HasIndex("IsActive");
|
||||||
|
|
||||||
|
b.ToTable("PointsRules", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.PointsRuleModel.PointsRuleCondition", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasComment("条件标识");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<int>("DimensionType")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasComment("维度类型");
|
||||||
|
|
||||||
|
b.Property<string>("DimensionValue")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)")
|
||||||
|
.HasComment("维度值");
|
||||||
|
|
||||||
|
b.Property<string>("Operator")
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)")
|
||||||
|
.HasComment("操作符");
|
||||||
|
|
||||||
|
b.Property<Guid>("RuleId")
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasComment("关联规则标识");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("RuleId", "DimensionType");
|
||||||
|
|
||||||
|
b.ToTable("PointsRuleConditions", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.Users.MemberEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasComment("会员标识");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created_at");
|
||||||
|
|
||||||
|
b.Property<string>("OpenId")
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("character varying(64)")
|
||||||
|
.HasColumnName("open_id");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)")
|
||||||
|
.HasColumnName("phone_number");
|
||||||
|
|
||||||
|
b.Property<string>("Status")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)")
|
||||||
|
.HasColumnName("status");
|
||||||
|
|
||||||
|
b.Property<long>("TenantId")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("tenant_id");
|
||||||
|
|
||||||
|
b.Property<string>("UnionId")
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("character varying(64)")
|
||||||
|
.HasColumnName("union_id");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("updated_at");
|
||||||
|
|
||||||
|
b.Property<int>("Version")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasDefaultValue(1)
|
||||||
|
.HasColumnName("version");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("OpenId")
|
||||||
|
.HasDatabaseName("idx_member_openid");
|
||||||
|
|
||||||
|
b.HasIndex("TenantId")
|
||||||
|
.HasDatabaseName("idx_member_tenantid");
|
||||||
|
|
||||||
|
b.HasIndex("UnionId")
|
||||||
|
.HasDatabaseName("idx_member_unionid");
|
||||||
|
|
||||||
|
b.HasIndex("TenantId", "PhoneNumber")
|
||||||
|
.HasDatabaseName("idx_member_tenant_phone");
|
||||||
|
|
||||||
|
b.ToTable("fls_member", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.Users.MemberTag", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created_at");
|
||||||
|
|
||||||
|
b.Property<Guid>("MemberId")
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasColumnName("member_id");
|
||||||
|
|
||||||
|
b.Property<string>("TagId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("character varying(50)")
|
||||||
|
.HasColumnName("tag_id");
|
||||||
|
|
||||||
|
b.Property<string>("TagName")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)")
|
||||||
|
.HasColumnName("tag_name");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("TagId")
|
||||||
|
.HasDatabaseName("idx_membertag_tagid");
|
||||||
|
|
||||||
|
b.HasIndex("MemberId", "TagId")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx_membertag_member_tag");
|
||||||
|
|
||||||
|
b.ToTable("fls_member_tag", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.Users.OAuthAuthorization", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("AccessToken")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<DateTime>("AuthorizedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastLoginAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<Guid>("MemberId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("OpenId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<int>("Provider")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("RefreshToken")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("TokenExpiredAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("UnionId")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("MemberId");
|
||||||
|
|
||||||
|
b.ToTable("OAuthAuthorization");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.Users.WechatAuthorization", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("AuthorizedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("authorized_at");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastLoginAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("last_login_at");
|
||||||
|
|
||||||
|
b.Property<long>("MemberId")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("member_id");
|
||||||
|
|
||||||
|
b.Property<string>("OpenId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("character varying(64)")
|
||||||
|
.HasColumnName("open_id");
|
||||||
|
|
||||||
|
b.Property<string>("UnionId")
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("character varying(64)")
|
||||||
|
.HasColumnName("union_id");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("MemberId")
|
||||||
|
.HasDatabaseName("idx_wechat_auth_memberid");
|
||||||
|
|
||||||
|
b.HasIndex("OpenId")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx_wechat_auth_openid");
|
||||||
|
|
||||||
|
b.HasIndex("UnionId")
|
||||||
|
.HasDatabaseName("idx_wechat_auth_unionid");
|
||||||
|
|
||||||
|
b.ToTable("fls_wechat_authorization", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NetCorePal.Extensions.DistributedTransactions.CAP.Persistence.CapLock", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Key")
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("character varying(128)");
|
||||||
|
|
||||||
|
b.Property<string>("Instance")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastLockTime")
|
||||||
|
.HasColumnType("TIMESTAMP");
|
||||||
|
|
||||||
|
b.HasKey("Key");
|
||||||
|
|
||||||
|
b.ToTable("CAPLock", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NetCorePal.Extensions.DistributedTransactions.CAP.Persistence.PublishedMessage", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("Added")
|
||||||
|
.HasColumnType("TIMESTAMP");
|
||||||
|
|
||||||
|
b.Property<string>("Content")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("ExpiresAt")
|
||||||
|
.HasColumnType("TIMESTAMP");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)");
|
||||||
|
|
||||||
|
b.Property<int?>("Retries")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("StatusName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(40)
|
||||||
|
.HasColumnType("character varying(40)");
|
||||||
|
|
||||||
|
b.Property<string>("Version")
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex(new[] { "ExpiresAt", "StatusName" }, "IX_ExpiresAt_StatusName");
|
||||||
|
|
||||||
|
b.HasIndex(new[] { "Version", "ExpiresAt", "StatusName" }, "IX_Version_ExpiresAt_StatusName");
|
||||||
|
|
||||||
|
b.ToTable("CAPPublishedMessage", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NetCorePal.Extensions.DistributedTransactions.CAP.Persistence.ReceivedMessage", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("Added")
|
||||||
|
.HasColumnType("TIMESTAMP");
|
||||||
|
|
||||||
|
b.Property<string>("Content")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("ExpiresAt")
|
||||||
|
.HasColumnType("TIMESTAMP");
|
||||||
|
|
||||||
|
b.Property<string>("Group")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(400)
|
||||||
|
.HasColumnType("character varying(400)");
|
||||||
|
|
||||||
|
b.Property<int?>("Retries")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("StatusName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("character varying(50)");
|
||||||
|
|
||||||
|
b.Property<string>("Version")
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex(new[] { "ExpiresAt", "StatusName" }, "IX_ExpiresAt_StatusName")
|
||||||
|
.HasDatabaseName("IX_ExpiresAt_StatusName1");
|
||||||
|
|
||||||
|
b.HasIndex(new[] { "Version", "ExpiresAt", "StatusName" }, "IX_Version_ExpiresAt_StatusName")
|
||||||
|
.HasDatabaseName("IX_Version_ExpiresAt_StatusName1");
|
||||||
|
|
||||||
|
b.ToTable("CAPReceivedMessage", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.PointsModel.PointsTransaction", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Fengling.Member.Domain.Aggregates.PointsModel.PointsAccount", null)
|
||||||
|
.WithMany("Transactions")
|
||||||
|
.HasForeignKey("PointsAccountId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.PointsRuleModel.PointsRuleCondition", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Fengling.Member.Domain.Aggregates.PointsRuleModel.PointsRule", null)
|
||||||
|
.WithMany("Conditions")
|
||||||
|
.HasForeignKey("RuleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.Users.MemberTag", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Fengling.Member.Domain.Aggregates.Users.MemberEntity", null)
|
||||||
|
.WithMany("Tags")
|
||||||
|
.HasForeignKey("MemberId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.Users.OAuthAuthorization", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Fengling.Member.Domain.Aggregates.Users.MemberEntity", null)
|
||||||
|
.WithMany("OAuthAuthorizations")
|
||||||
|
.HasForeignKey("MemberId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.PointsModel.PointsAccount", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Transactions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.PointsRuleModel.PointsRule", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Conditions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.Users.MemberEntity", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("OAuthAuthorizations");
|
||||||
|
|
||||||
|
b.Navigation("Tags");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,72 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Fengling.Member.Infrastructure.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class ChangeMemberIdToGuid : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AlterColumn<Guid>(
|
||||||
|
name: "MemberId",
|
||||||
|
table: "OAuthAuthorization",
|
||||||
|
type: "uuid",
|
||||||
|
nullable: false,
|
||||||
|
oldClrType: typeof(long),
|
||||||
|
oldType: "bigint");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<Guid>(
|
||||||
|
name: "member_id",
|
||||||
|
table: "fls_member_tag",
|
||||||
|
type: "uuid",
|
||||||
|
nullable: false,
|
||||||
|
oldClrType: typeof(long),
|
||||||
|
oldType: "bigint");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<Guid>(
|
||||||
|
name: "id",
|
||||||
|
table: "fls_member",
|
||||||
|
type: "uuid",
|
||||||
|
nullable: false,
|
||||||
|
comment: "会员标识",
|
||||||
|
oldClrType: typeof(long),
|
||||||
|
oldType: "bigint")
|
||||||
|
.OldAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AlterColumn<long>(
|
||||||
|
name: "MemberId",
|
||||||
|
table: "OAuthAuthorization",
|
||||||
|
type: "bigint",
|
||||||
|
nullable: false,
|
||||||
|
oldClrType: typeof(Guid),
|
||||||
|
oldType: "uuid");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<long>(
|
||||||
|
name: "member_id",
|
||||||
|
table: "fls_member_tag",
|
||||||
|
type: "bigint",
|
||||||
|
nullable: false,
|
||||||
|
oldClrType: typeof(Guid),
|
||||||
|
oldType: "uuid");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<long>(
|
||||||
|
name: "id",
|
||||||
|
table: "fls_member",
|
||||||
|
type: "bigint",
|
||||||
|
nullable: false,
|
||||||
|
oldClrType: typeof(Guid),
|
||||||
|
oldType: "uuid",
|
||||||
|
oldComment: "会员标识")
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -233,12 +233,10 @@ namespace Fengling.Member.Infrastructure.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.Users.MemberEntity", b =>
|
modelBuilder.Entity("Fengling.Member.Domain.Aggregates.Users.MemberEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<long>("Id")
|
b.Property<Guid>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.HasColumnType("uuid")
|
||||||
.HasColumnType("bigint")
|
.HasColumnName("id")
|
||||||
.HasColumnName("id");
|
.HasComment("会员标识");
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
b.Property<DateTime>("CreatedAt")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp with time zone")
|
||||||
@ -310,8 +308,8 @@ namespace Fengling.Member.Infrastructure.Migrations
|
|||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp with time zone")
|
||||||
.HasColumnName("created_at");
|
.HasColumnName("created_at");
|
||||||
|
|
||||||
b.Property<long>("MemberId")
|
b.Property<Guid>("MemberId")
|
||||||
.HasColumnType("bigint")
|
.HasColumnType("uuid")
|
||||||
.HasColumnName("member_id");
|
.HasColumnName("member_id");
|
||||||
|
|
||||||
b.Property<string>("TagId")
|
b.Property<string>("TagId")
|
||||||
@ -354,8 +352,8 @@ namespace Fengling.Member.Infrastructure.Migrations
|
|||||||
b.Property<DateTime?>("LastLoginAt")
|
b.Property<DateTime?>("LastLoginAt")
|
||||||
.HasColumnType("timestamp with time zone");
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
b.Property<long>("MemberId")
|
b.Property<Guid>("MemberId")
|
||||||
.HasColumnType("bigint");
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
b.Property<string>("OpenId")
|
b.Property<string>("OpenId")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
|
|||||||
@ -2,35 +2,51 @@ using Fengling.Member.Domain.Aggregates.Users;
|
|||||||
|
|
||||||
namespace Fengling.Member.Infrastructure.Repositories;
|
namespace Fengling.Member.Infrastructure.Repositories;
|
||||||
|
|
||||||
public interface IMemberRepository : IRepository<Fengling.Member.Domain.Aggregates.Users.MemberEntity, long>
|
public interface IMemberRepository : IRepository<Fengling.Member.Domain.Aggregates.Users.MemberEntity, MemberId>
|
||||||
{
|
{
|
||||||
Task<Fengling.Member.Domain.Aggregates.Users.MemberEntity?> GetByPhoneNumberAsync(long tenantId, string phoneNumber, CancellationToken cancellationToken = default);
|
Task<Fengling.Member.Domain.Aggregates.Users.MemberEntity?> GetByPhoneNumberAsync(long tenantId, string phoneNumber,
|
||||||
Task<Fengling.Member.Domain.Aggregates.Users.MemberEntity?> GetByOpenIdAsync(string openId, CancellationToken cancellationToken = default);
|
CancellationToken cancellationToken = default);
|
||||||
Task<Fengling.Member.Domain.Aggregates.Users.MemberEntity?> GetByUnionIdAsync(string unionId, CancellationToken cancellationToken = default);
|
|
||||||
Task<IEnumerable<Fengling.Member.Domain.Aggregates.Users.MemberEntity>> GetByTenantIdAsync(long tenantId, int page = 1, int pageSize = 20, CancellationToken cancellationToken = default);
|
Task<Fengling.Member.Domain.Aggregates.Users.MemberEntity?> GetByOpenIdAsync(string openId,
|
||||||
Task<bool> ExistsByPhoneNumberAsync(long tenantId, string phoneNumber, CancellationToken cancellationToken = default);
|
CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
Task<Fengling.Member.Domain.Aggregates.Users.MemberEntity?> GetByUnionIdAsync(string unionId,
|
||||||
|
CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
Task<IEnumerable<Fengling.Member.Domain.Aggregates.Users.MemberEntity>> GetByTenantIdAsync(long tenantId,
|
||||||
|
int page = 1, int pageSize = 20, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
Task<bool> ExistsByPhoneNumberAsync(long tenantId, string phoneNumber,
|
||||||
|
CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
Task<bool> ExistsByOpenIdAsync(string openId, CancellationToken cancellationToken = default);
|
Task<bool> ExistsByOpenIdAsync(string openId, CancellationToken cancellationToken = default);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MemberRepository(ApplicationDbContext context) :
|
public class MemberRepository(ApplicationDbContext context)
|
||||||
RepositoryBase<Fengling.Member.Domain.Aggregates.Users.MemberEntity, long, ApplicationDbContext>(context), IMemberRepository
|
: RepositoryBase<Fengling.Member.Domain.Aggregates.Users.MemberEntity, MemberId, ApplicationDbContext>(context),
|
||||||
|
IMemberRepository
|
||||||
{
|
{
|
||||||
public async Task<Fengling.Member.Domain.Aggregates.Users.MemberEntity?> GetByPhoneNumberAsync(long tenantId, string phoneNumber, CancellationToken cancellationToken = default)
|
public async Task<Fengling.Member.Domain.Aggregates.Users.MemberEntity?> GetByPhoneNumberAsync(long tenantId,
|
||||||
|
string phoneNumber, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
return await DbContext.Members.FirstOrDefaultAsync(m => m.TenantId == tenantId && m.PhoneNumber == phoneNumber, cancellationToken);
|
return await DbContext.Members.FirstOrDefaultAsync(m => m.TenantId == tenantId && m.PhoneNumber == phoneNumber,
|
||||||
|
cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Fengling.Member.Domain.Aggregates.Users.MemberEntity?> GetByOpenIdAsync(string openId, CancellationToken cancellationToken = default)
|
public async Task<Fengling.Member.Domain.Aggregates.Users.MemberEntity?> GetByOpenIdAsync(string openId,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
return await DbContext.Members.FirstOrDefaultAsync(m => m.OpenId == openId, cancellationToken);
|
return await DbContext.Members.FirstOrDefaultAsync(m => m.OpenId == openId, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Fengling.Member.Domain.Aggregates.Users.MemberEntity?> GetByUnionIdAsync(string unionId, CancellationToken cancellationToken = default)
|
public async Task<Fengling.Member.Domain.Aggregates.Users.MemberEntity?> GetByUnionIdAsync(string unionId,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
return await DbContext.Members.FirstOrDefaultAsync(m => m.UnionId == unionId, cancellationToken);
|
return await DbContext.Members.FirstOrDefaultAsync(m => m.UnionId == unionId, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<Fengling.Member.Domain.Aggregates.Users.MemberEntity>> GetByTenantIdAsync(long tenantId, int page = 1, int pageSize = 20, CancellationToken cancellationToken = default)
|
public async Task<IEnumerable<Fengling.Member.Domain.Aggregates.Users.MemberEntity>> GetByTenantIdAsync(
|
||||||
|
long tenantId, int page = 1, int pageSize = 20, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
return await DbContext.Members
|
return await DbContext.Members
|
||||||
.Where(m => m.TenantId == tenantId)
|
.Where(m => m.TenantId == tenantId)
|
||||||
@ -40,9 +56,11 @@ public class MemberRepository(ApplicationDbContext context) :
|
|||||||
.ToListAsync(cancellationToken);
|
.ToListAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> ExistsByPhoneNumberAsync(long tenantId, string phoneNumber, CancellationToken cancellationToken = default)
|
public async Task<bool> ExistsByPhoneNumberAsync(long tenantId, string phoneNumber,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
return await DbContext.Members.AnyAsync(m => m.TenantId == tenantId && m.PhoneNumber == phoneNumber, cancellationToken);
|
return await DbContext.Members.AnyAsync(m => m.TenantId == tenantId && m.PhoneNumber == phoneNumber,
|
||||||
|
cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> ExistsByOpenIdAsync(string openId, CancellationToken cancellationToken = default)
|
public async Task<bool> ExistsByOpenIdAsync(string openId, CancellationToken cancellationToken = default)
|
||||||
|
|||||||
@ -2,18 +2,13 @@ using FastEndpoints;
|
|||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Fengling.Member.Application.Commands.Member;
|
using Fengling.Member.Application.Commands.Member;
|
||||||
|
using Fengling.Member.Domain.Aggregates.Users;
|
||||||
|
|
||||||
namespace Fengling.Member.Web.Endpoints.v1;
|
namespace Fengling.Member.Web.Endpoints.v1;
|
||||||
|
|
||||||
public class BindAlipayEndpoint : Endpoint<BindAlipayRequest, BindAlipayResponse>
|
public class BindAlipayEndpoint(IMediator mediator)
|
||||||
|
: Endpoint<BindAlipayRequest, BindAlipayResponse>
|
||||||
{
|
{
|
||||||
private readonly IMediator _mediator;
|
|
||||||
|
|
||||||
public BindAlipayEndpoint(IMediator mediator)
|
|
||||||
{
|
|
||||||
_mediator = mediator;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Configure()
|
public override void Configure()
|
||||||
{
|
{
|
||||||
Post("/api/v1/members/{MemberId}/alipay");
|
Post("/api/v1/members/{MemberId}/alipay");
|
||||||
@ -26,37 +21,13 @@ public class BindAlipayEndpoint : Endpoint<BindAlipayRequest, BindAlipayResponse
|
|||||||
|
|
||||||
public override async Task HandleAsync(BindAlipayRequest req, CancellationToken ct)
|
public override async Task HandleAsync(BindAlipayRequest req, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var command = new BindAlipayCommand
|
var command = new BindAlipayCommand(req.MemberId, req.AlipayOpenId, req.AlipayUserId);
|
||||||
{
|
|
||||||
MemberId = req.MemberId,
|
|
||||||
AlipayOpenId = req.AlipayOpenId,
|
|
||||||
AlipayUserId = req.AlipayUserId
|
|
||||||
};
|
|
||||||
|
|
||||||
var result = await _mediator.Send(command, ct);
|
var result = await mediator.Send(command, ct);
|
||||||
|
|
||||||
Response = new BindAlipayResponse
|
Response = new BindAlipayResponse(result.MemberId, result.AlipayOpenId, result.AlipayUserId, result.BoundAt);
|
||||||
{
|
|
||||||
MemberId = result.MemberId,
|
|
||||||
AlipayOpenId = result.AlipayOpenId,
|
|
||||||
AlipayUserId = result.AlipayUserId,
|
|
||||||
BoundAt = result.BoundAt
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BindAlipayRequest
|
public record BindAlipayRequest([FromRoute] MemberId MemberId, string AlipayOpenId, string? AlipayUserId);
|
||||||
{
|
public record BindAlipayResponse(MemberId MemberId, string AlipayOpenId, string? AlipayUserId, DateTime BoundAt);
|
||||||
[FromRoute]
|
|
||||||
public long MemberId { get; set; }
|
|
||||||
public string AlipayOpenId { get; set; } = string.Empty;
|
|
||||||
public string? AlipayUserId { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class BindAlipayResponse
|
|
||||||
{
|
|
||||||
public long MemberId { get; set; }
|
|
||||||
public string AlipayOpenId { get; set; } = string.Empty;
|
|
||||||
public string? AlipayUserId { get; set; }
|
|
||||||
public DateTime BoundAt { get; set; }
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,18 +1,13 @@
|
|||||||
using FastEndpoints;
|
using FastEndpoints;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using Fengling.Member.Application.Commands.Member;
|
using Fengling.Member.Application.Commands.Member;
|
||||||
|
using Fengling.Member.Domain.Aggregates.Users;
|
||||||
|
|
||||||
namespace Fengling.Member.Web.Endpoints.v1;
|
namespace Fengling.Member.Web.Endpoints.v1;
|
||||||
|
|
||||||
public class RegisterMemberEndpoint : Endpoint<RegisterMemberRequest, RegisterMemberResponse>
|
public class RegisterMemberEndpoint(IMediator mediator)
|
||||||
|
: Endpoint<RegisterMemberRequest, RegisterMemberResponse>
|
||||||
{
|
{
|
||||||
private readonly IMediator _mediator;
|
|
||||||
|
|
||||||
public RegisterMemberEndpoint(IMediator mediator)
|
|
||||||
{
|
|
||||||
_mediator = mediator;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Configure()
|
public override void Configure()
|
||||||
{
|
{
|
||||||
Post("/api/v1/members");
|
Post("/api/v1/members");
|
||||||
@ -26,44 +21,13 @@ public class RegisterMemberEndpoint : Endpoint<RegisterMemberRequest, RegisterMe
|
|||||||
|
|
||||||
public override async Task HandleAsync(RegisterMemberRequest req, CancellationToken ct)
|
public override async Task HandleAsync(RegisterMemberRequest req, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var command = new RegisterMemberCommand
|
var command = new RegisterMemberCommand(req.TenantId, req.PhoneNumber, req.OpenId, req.UnionId, req.Source);
|
||||||
{
|
|
||||||
TenantId = req.TenantId,
|
|
||||||
PhoneNumber = req.PhoneNumber,
|
|
||||||
OpenId = req.OpenId,
|
|
||||||
UnionId = req.UnionId,
|
|
||||||
Source = req.Source
|
|
||||||
};
|
|
||||||
|
|
||||||
var result = await _mediator.Send(command, ct);
|
var result = await mediator.Send(command, ct);
|
||||||
|
|
||||||
Response = new RegisterMemberResponse
|
Response = new RegisterMemberResponse(result.MemberId, result.TenantId, result.PhoneNumber, result.OpenId, result.Status.ToString(), result.RegisteredAt);
|
||||||
{
|
|
||||||
MemberId = result.MemberId,
|
|
||||||
TenantId = result.TenantId,
|
|
||||||
PhoneNumber = result.PhoneNumber,
|
|
||||||
OpenId = result.OpenId,
|
|
||||||
Status = result.Status.ToString(),
|
|
||||||
RegisteredAt = result.RegisteredAt
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class RegisterMemberRequest
|
public record RegisterMemberRequest(long TenantId, string? PhoneNumber, string? OpenId, string? UnionId, string? Source);
|
||||||
{
|
public record RegisterMemberResponse(MemberId MemberId, long TenantId, string? PhoneNumber, string? OpenId, string Status, DateTime RegisteredAt);
|
||||||
public long TenantId { get; set; }
|
|
||||||
public string? PhoneNumber { get; set; }
|
|
||||||
public string? OpenId { get; set; }
|
|
||||||
public string? UnionId { get; set; }
|
|
||||||
public string? Source { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class RegisterMemberResponse
|
|
||||||
{
|
|
||||||
public long MemberId { get; set; }
|
|
||||||
public long TenantId { get; set; }
|
|
||||||
public string? PhoneNumber { get; set; }
|
|
||||||
public string? OpenId { get; set; }
|
|
||||||
public string Status { get; set; } = string.Empty;
|
|
||||||
public DateTime RegisteredAt { get; set; }
|
|
||||||
}
|
|
||||||
|
|||||||
@ -2,18 +2,13 @@ using FastEndpoints;
|
|||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Fengling.Member.Application.Commands.Member;
|
using Fengling.Member.Application.Commands.Member;
|
||||||
|
using Fengling.Member.Domain.Aggregates.Users;
|
||||||
|
|
||||||
namespace Fengling.Member.Web.Endpoints.v1;
|
namespace Fengling.Member.Web.Endpoints.v1;
|
||||||
|
|
||||||
public class BindOAuthEndpoint : Endpoint<BindOAuthRequest, BindOAuthResponse>
|
public class BindOAuthEndpoint(IMediator mediator)
|
||||||
|
: Endpoint<BindOAuthRequest, BindOAuthResponse>
|
||||||
{
|
{
|
||||||
private readonly IMediator _mediator;
|
|
||||||
|
|
||||||
public BindOAuthEndpoint(IMediator mediator)
|
|
||||||
{
|
|
||||||
_mediator = mediator;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Configure()
|
public override void Configure()
|
||||||
{
|
{
|
||||||
Post("/api/v1/members/{MemberId}/oauth");
|
Post("/api/v1/members/{MemberId}/oauth");
|
||||||
@ -26,42 +21,13 @@ public class BindOAuthEndpoint : Endpoint<BindOAuthRequest, BindOAuthResponse>
|
|||||||
|
|
||||||
public override async Task HandleAsync(BindOAuthRequest req, CancellationToken ct)
|
public override async Task HandleAsync(BindOAuthRequest req, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var command = new BindOAuthCommand
|
var command = new BindOAuthCommand(req.Provider, req.MemberId, req.OpenId, req.UnionId);
|
||||||
{
|
|
||||||
MemberId = req.MemberId,
|
|
||||||
Provider = req.Provider,
|
|
||||||
OpenId = req.OpenId,
|
|
||||||
UnionId = req.UnionId
|
|
||||||
};
|
|
||||||
|
|
||||||
var result = await _mediator.Send(command, ct);
|
var result = await mediator.Send(command, ct);
|
||||||
|
|
||||||
Response = new BindOAuthResponse
|
Response = new BindOAuthResponse(result.MemberId, result.Provider, result.OpenId, result.UnionId, result.BoundAt);
|
||||||
{
|
|
||||||
MemberId = result.MemberId,
|
|
||||||
Provider = result.Provider,
|
|
||||||
OpenId = result.OpenId,
|
|
||||||
UnionId = result.UnionId,
|
|
||||||
BoundAt = result.BoundAt
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BindOAuthRequest
|
public record BindOAuthRequest([FromRoute] MemberId MemberId, [FromRoute] OAuthProvider Provider, string OpenId, string? UnionId);
|
||||||
{
|
public record BindOAuthResponse(MemberId MemberId, OAuthProvider Provider, string OpenId, string? UnionId, DateTime BoundAt);
|
||||||
[FromRoute]
|
|
||||||
public long MemberId { get; set; }
|
|
||||||
[FromRoute]
|
|
||||||
public Domain.Aggregates.Users.OAuthProvider Provider { get; set; }
|
|
||||||
public string OpenId { get; set; } = string.Empty;
|
|
||||||
public string? UnionId { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class BindOAuthResponse
|
|
||||||
{
|
|
||||||
public long MemberId { get; set; }
|
|
||||||
public Domain.Aggregates.Users.OAuthProvider Provider { get; set; }
|
|
||||||
public string OpenId { get; set; } = string.Empty;
|
|
||||||
public string? UnionId { get; set; }
|
|
||||||
public DateTime BoundAt { get; set; }
|
|
||||||
}
|
|
||||||
|
|||||||
@ -2,18 +2,13 @@ using FastEndpoints;
|
|||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Fengling.Member.Application.Commands.Member;
|
using Fengling.Member.Application.Commands.Member;
|
||||||
|
using Fengling.Member.Domain.Aggregates.Users;
|
||||||
|
|
||||||
namespace Fengling.Member.Web.Endpoints.v1;
|
namespace Fengling.Member.Web.Endpoints.v1;
|
||||||
|
|
||||||
public class BindWechatEndpoint : Endpoint<BindWechatRequest, BindWechatResponse>
|
public class BindWechatEndpoint(IMediator mediator)
|
||||||
|
: Endpoint<BindWechatRequest, BindWechatResponse>
|
||||||
{
|
{
|
||||||
private readonly IMediator _mediator;
|
|
||||||
|
|
||||||
public BindWechatEndpoint(IMediator mediator)
|
|
||||||
{
|
|
||||||
_mediator = mediator;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Configure()
|
public override void Configure()
|
||||||
{
|
{
|
||||||
Post("/api/v1/members/{MemberId}/wechat");
|
Post("/api/v1/members/{MemberId}/wechat");
|
||||||
@ -26,37 +21,13 @@ public class BindWechatEndpoint : Endpoint<BindWechatRequest, BindWechatResponse
|
|||||||
|
|
||||||
public override async Task HandleAsync(BindWechatRequest req, CancellationToken ct)
|
public override async Task HandleAsync(BindWechatRequest req, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var command = new BindWechatCommand
|
var command = new BindWechatCommand(req.MemberId, req.OpenId, req.UnionId);
|
||||||
{
|
|
||||||
MemberId = req.MemberId,
|
|
||||||
OpenId = req.OpenId,
|
|
||||||
UnionId = req.UnionId
|
|
||||||
};
|
|
||||||
|
|
||||||
var result = await _mediator.Send(command, ct);
|
var result = await mediator.Send(command, ct);
|
||||||
|
|
||||||
Response = new BindWechatResponse
|
Response = new BindWechatResponse(result.MemberId, result.OpenId, result.UnionId, result.BoundAt);
|
||||||
{
|
|
||||||
MemberId = result.MemberId,
|
|
||||||
OpenId = result.OpenId,
|
|
||||||
UnionId = result.UnionId,
|
|
||||||
BoundAt = result.BoundAt
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BindWechatRequest
|
public record BindWechatRequest([FromRoute] MemberId MemberId, string OpenId, string? UnionId);
|
||||||
{
|
public record BindWechatResponse(MemberId MemberId, string OpenId, string? UnionId, DateTime BoundAt);
|
||||||
[FromRoute]
|
|
||||||
public long MemberId { get; set; }
|
|
||||||
public string OpenId { get; set; } = string.Empty;
|
|
||||||
public string? UnionId { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class BindWechatResponse
|
|
||||||
{
|
|
||||||
public long MemberId { get; set; }
|
|
||||||
public string OpenId { get; set; } = string.Empty;
|
|
||||||
public string? UnionId { get; set; }
|
|
||||||
public DateTime BoundAt { get; set; }
|
|
||||||
}
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user