Project.Fengling.QoderVersion/Backend/src/Fengling.Backend.Web/Application/Commands/Members/LoginMemberCommand.cs
sam 056eb9b6f9 feat: 实现完整的前后端功能
- 后端新增管理员、商品、分类聚合模型
- 实现积分规则、礼品、订单、会员等完整功能
- 添加管理员认证和权限管理
- 完善数据库迁移和实体配置
- 前端管理后台实现登录、仪表盘、积分规则、礼品、订单、会员等页面
- 集成shadcn-vue UI组件库
- 添加前后端功能文档和截图
2026-02-11 21:36:37 +08:00

104 lines
3.5 KiB
C#

using Fengling.Backend.Domain.AggregatesModel.MemberAggregate;
using Fengling.Backend.Infrastructure.Repositories;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Fengling.Backend.Web.Utils;
namespace Fengling.Backend.Web.Application.Commands.Members;
/// <summary>
/// 会员登录命令
/// </summary>
public record LoginMemberCommand(string Phone, string Password) : ICommand<LoginMemberResponse>;
/// <summary>
/// 登录响应
/// </summary>
public record LoginMemberResponse(MemberId MemberId, string Token);
/// <summary>
/// 会员登录命令验证器
/// </summary>
public class LoginMemberCommandValidator : AbstractValidator<LoginMemberCommand>
{
public LoginMemberCommandValidator()
{
RuleFor(x => x.Phone)
.NotEmpty().WithMessage("手机号不能为空");
RuleFor(x => x.Password)
.NotEmpty().WithMessage("密码不能为空");
}
}
/// <summary>
/// 会员登录命令处理器
/// </summary>
public class LoginMemberCommandHandler(
IMemberRepository memberRepository,
IConfiguration configuration)
: ICommandHandler<LoginMemberCommand, LoginMemberResponse>
{
public async Task<LoginMemberResponse> Handle(LoginMemberCommand command, CancellationToken cancellationToken)
{
// 查询会员
var member = await memberRepository.GetByPhoneAsync(command.Phone, cancellationToken)
?? throw new KnownException("手机号或密码错误");
// 验证密码
var hashedPassword = HashPassword(command.Password);
if (member.Password != hashedPassword)
throw new KnownException("手机号或密码错误");
// 检查状态
if (member.Status == MemberStatus.Disabled)
throw new KnownException("该账号已被禁用");
// 生成JWT Token
var token = GenerateJwtToken(member.Id, member.Phone);
return new LoginMemberResponse(member.Id, token);
}
private static string HashPassword(string password)
{
return Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(password));
}
private string GenerateJwtToken(MemberId memberId, string phone)
{
var appConfig = configuration.GetSection("AppConfiguration").Get<AppConfiguration>()
?? new AppConfiguration
{
JwtIssuer = "FenglingBackend",
JwtAudience = "FenglingBackend",
Secret = "YourVerySecretKeyForJwtTokenGeneration12345!",
TokenExpiryInMinutes = 1440 // 24小时
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(
appConfig.Secret.Length >= 32 ? appConfig.Secret : "YourVerySecretKeyForJwtTokenGeneration12345!"));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, memberId.ToString()),
new Claim(ClaimTypes.Name, phone),
new Claim(ClaimTypes.Role, "Member"),
new Claim("member_id", memberId.ToString())
};
var token = new JwtSecurityToken(
issuer: appConfig.JwtIssuer,
audience: appConfig.JwtAudience,
claims: claims,
expires: DateTime.UtcNow.AddMinutes(appConfig.TokenExpiryInMinutes),
signingCredentials: creds);
return new JwtSecurityTokenHandler().WriteToken(token);
}
}