添加项目文件。
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
using AutoMapper;
|
||||
using IdentityService.WebApi.Applications.Dtos.Common;
|
||||
|
||||
namespace IdentityService.WebApi.Applications.Auth
|
||||
{
|
||||
public class AuthMappingProfile : Profile
|
||||
{
|
||||
public AuthMappingProfile()
|
||||
{
|
||||
CreateMap<Domain.Entities.User, UserResponse>()
|
||||
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id))
|
||||
.ForMember(dest => dest.UserName, opt => opt.MapFrom(src => src.UserName))
|
||||
.ForMember(dest => dest.Email, opt => opt.MapFrom(src => src.Email))
|
||||
.ForMember(dest => dest.Phone, opt => opt.MapFrom(src => src.PhoneNumber))
|
||||
.ForMember(dest => dest.Region, opt => opt.MapFrom(src => src.Region))
|
||||
.ForMember(dest => dest.Description, opt => opt.MapFrom(src => src.Description))
|
||||
.ForMember(dest => dest.Avatar, opt => opt.MapFrom(src => src.Avatar))
|
||||
.ForMember(dest => dest.CreationTime, opt => opt.MapFrom(src => src.CreationTime))
|
||||
.ForMember(dest => dest.Deletion, opt => opt.MapFrom(src => src.Deletion))
|
||||
.ForMember(dest => dest.NickName, opt => opt.MapFrom(src => src.NickName))
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
using AutoMapper;
|
||||
using IdentityService.Domain;
|
||||
using IdentityService.WebApi.Applications.Dtos;
|
||||
using IdentityService.WebApi.Applications.Dtos.Common;
|
||||
using IM.Commons;
|
||||
using IM.Jwt;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace IdentityService.WebApi.Applications.Auth
|
||||
{
|
||||
public class AuthService
|
||||
{
|
||||
private readonly ITokenService tokenService;
|
||||
private readonly IIdRepository idRepository;
|
||||
private readonly IOptions<JwtOptions> jwtOptions;
|
||||
private readonly IdDomainService idDomainService;
|
||||
private readonly IMapper mapper;
|
||||
|
||||
public AuthService(ITokenService tokenService, IIdRepository idRepository,
|
||||
IOptions<JwtOptions> options, IdDomainService idDomainService,
|
||||
IMapper mapper
|
||||
)
|
||||
{
|
||||
this.tokenService = tokenService;
|
||||
this.idRepository = idRepository;
|
||||
jwtOptions = options;
|
||||
this.idDomainService = idDomainService;
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
public async Task<Result<LoginResponse>> LoginAsync(string username, string password)
|
||||
{
|
||||
var user = await idRepository.FindByUserNameAsync(username);
|
||||
|
||||
if (user is null)
|
||||
{
|
||||
return Result<LoginResponse>.Fail(ResultCode.USER_NOT_FOUND);
|
||||
}
|
||||
var idResult = await idRepository.CheckForSignInAsync(user, password, true);
|
||||
if (!idResult.Succeeded)
|
||||
{
|
||||
return Result<LoginResponse>.Fail(ResultCode.PASSWORD_ERROR);
|
||||
}
|
||||
var token = await BuildTokenAsync(user);
|
||||
var refreshToken = await tokenService.CreateRefreshTokenAsync(user.Id);
|
||||
return Result<LoginResponse>.Success(new LoginResponse(user.Id, token, refreshToken, null));
|
||||
}
|
||||
public async Task<Result<UserResponse?>> RegisterAsync(string userName, string password, string nickName)
|
||||
{
|
||||
var userResult = await idDomainService.CreateUserAsync(userName, password, nickName);
|
||||
if (!userResult.Succeeded)
|
||||
return Result<UserResponse?>.Fail(userResult);
|
||||
|
||||
var idResult = await idRepository.CreateAsync(userResult.Data!, password);
|
||||
if (!idResult.Succeeded)
|
||||
{
|
||||
var msg = idResult.Errors.FirstOrDefault();
|
||||
return Result<UserResponse?>.Fail(
|
||||
ResultCode.REGISTER_ERROR,
|
||||
msg?.Description ?? ResultCode.REGISTER_ERROR.GetDescription());
|
||||
}
|
||||
|
||||
return Result<UserResponse?>.Success(mapper.Map<UserResponse>(userResult.Data));
|
||||
}
|
||||
public async Task<Result<LoginResponse>> RefreshAsync(string refreshToken)
|
||||
{
|
||||
var validateRes = await tokenService.ValidateRefreshTokenAsync(refreshToken);
|
||||
if (!validateRes.ok)
|
||||
{
|
||||
return Result<LoginResponse>.Fail(ResultCode.AUTH_FAILED);
|
||||
}
|
||||
|
||||
var user = await idRepository.FindByIdAsync(validateRes.userId);
|
||||
|
||||
if (user is null)
|
||||
{
|
||||
return Result<LoginResponse>.Fail(ResultCode.USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
var token = await BuildTokenAsync(user);
|
||||
|
||||
return Result<LoginResponse>.Success(new LoginResponse(user.Id, token, refreshToken, null));
|
||||
}
|
||||
private async Task<string> BuildTokenAsync(Domain.Entities.User user)
|
||||
{
|
||||
var roles = await idRepository.GetRolesAsync(user);
|
||||
List<Claim> claims = new List<Claim>();
|
||||
claims.Add(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()));
|
||||
foreach (string role in roles)
|
||||
{
|
||||
claims.Add(new Claim(ClaimTypes.Role, role));
|
||||
}
|
||||
return tokenService.GetToken(claims, jwtOptions.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace IdentityService.WebApi.Applications.Dtos.Common
|
||||
{
|
||||
public class UserResponse
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string UserName { get; set; }
|
||||
public string NickName { get; set; }
|
||||
public string? Email { get; set; }
|
||||
public string? Phone { get; set; }
|
||||
public string Region { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string? Avatar { get; set; }
|
||||
public DateTimeOffset CreationTime { get; set; }
|
||||
public DateTimeOffset? Deletion { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace IdentityService.WebApi.Applications.Dtos
|
||||
{
|
||||
public class LoginResponse
|
||||
{
|
||||
public Guid UserId { get; init; }
|
||||
public string Token { get; init; }
|
||||
public string RefreshToken { get; init; }
|
||||
public DateTime? Expired { get; init; }
|
||||
|
||||
public LoginResponse(Guid userId, string token, string refreshToken, DateTime? expired)
|
||||
{
|
||||
UserId = userId;
|
||||
Token = token;
|
||||
RefreshToken = refreshToken;
|
||||
Expired = expired;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using IdentityService.Domain.Events;
|
||||
using IM.Commons.IntegrationEvents;
|
||||
using MassTransit;
|
||||
using MediatR;
|
||||
|
||||
namespace IdentityService.WebApi.Applications.EventHandler
|
||||
{
|
||||
public class UserProfileUpdateHandler : INotificationHandler<UserProfileUpdateDomainEvent>
|
||||
{
|
||||
private readonly IPublishEndpoint endpoint;
|
||||
|
||||
public UserProfileUpdateHandler(IPublishEndpoint endpoint)
|
||||
{
|
||||
this.endpoint = endpoint;
|
||||
}
|
||||
|
||||
public async Task Handle(UserProfileUpdateDomainEvent notification, CancellationToken cancellationToken)
|
||||
{
|
||||
await endpoint.Publish(new UserProfileUpdateEvent
|
||||
{
|
||||
CorrelationId = notification.User.Id,
|
||||
Avatar = notification.User.Avatar,
|
||||
Description = notification.User.Description,
|
||||
Email = notification.User.Email,
|
||||
NickName = notification.User.NickName,
|
||||
Phone = notification.User.PhoneNumber,
|
||||
Status = notification.User.Status.ToString(),
|
||||
UserId = notification.User.Id
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using AutoMapper;
|
||||
using IdentityService.WebApi.Applications.Dtos.Common;
|
||||
using IM.Commons;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace IdentityService.WebApi.Applications.User
|
||||
{
|
||||
public class UserResponseFindSpecification : ISpecification<Domain.Entities.User, UserResponse>
|
||||
{
|
||||
private readonly IEnumerable<Guid> Ids;
|
||||
public UserResponseFindSpecification(IEnumerable<Guid> ids, IMapper mapper)
|
||||
{
|
||||
Ids = ids;
|
||||
Criteria = u => Ids.Contains(u.Id);
|
||||
Select = u => new UserResponse
|
||||
{
|
||||
Avatar = u.Avatar,
|
||||
CreationTime = u.CreationTime,
|
||||
Deletion = u.Deletion,
|
||||
Description = u.Description,
|
||||
Email = u.Email,
|
||||
Id = u.Id,
|
||||
Phone = u.PhoneNumber,
|
||||
Region = u.Region,
|
||||
UserName = u.UserName
|
||||
};
|
||||
}
|
||||
|
||||
public Expression<Func<Domain.Entities.User, bool>> Criteria { get; }
|
||||
|
||||
public List<Expression<Func<Domain.Entities.User, object>>> Includes { get; }
|
||||
|
||||
public Expression<Func<Domain.Entities.User, UserResponse>> Select { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using AutoMapper;
|
||||
using IdentityService.Domain;
|
||||
using IdentityService.Infrastructure;
|
||||
using IdentityService.WebApi.Applications.Dtos.Common;
|
||||
using IM.Commons;
|
||||
|
||||
namespace IdentityService.WebApi.Applications.User
|
||||
{
|
||||
public class UserService
|
||||
{
|
||||
private readonly IIdRepository repository;
|
||||
private readonly IMapper mapper;
|
||||
private readonly UserDbContext userDb;
|
||||
|
||||
public UserService(IIdRepository repository, IMapper mapper,
|
||||
UserDbContext userDbContext
|
||||
)
|
||||
{
|
||||
this.repository = repository;
|
||||
this.mapper = mapper;
|
||||
userDb = userDbContext;
|
||||
}
|
||||
|
||||
public async Task<Result<UserResponse?>> GetUserInfoAsync(Guid userId)
|
||||
{
|
||||
var user = await repository.FindByIdAsync(userId);
|
||||
if (user is null)
|
||||
{
|
||||
return Result<UserResponse?>.Fail(ResultCode.USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
return Result<UserResponse?>.Success(mapper.Map<UserResponse>(user));
|
||||
}
|
||||
|
||||
public async Task<Result<UserResponse>> UpdateAsync(UserUpdateCommand command)
|
||||
{
|
||||
var user = await repository.FindByIdAsync(command.UserId);
|
||||
if (user is null)
|
||||
{
|
||||
return Result<UserResponse>.Fail(ResultCode.USER_NOT_FOUND);
|
||||
}
|
||||
user.Update(command.NickName, command.Region, command.Avatar, command.Description);
|
||||
|
||||
return Result<UserResponse>.Success(mapper.Map<UserResponse>(user));
|
||||
}
|
||||
|
||||
public async Task<Result<List<UserResponse>>> GetUsersByIdsAsync(IEnumerable<Guid> ids)
|
||||
{
|
||||
var specification = new UserResponseFindSpecification(ids, mapper);
|
||||
var users = await repository.GetUsersAsync(specification);
|
||||
return Result<List<UserResponse>>.Success([.. users]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace IdentityService.WebApi.Applications.User
|
||||
{
|
||||
public class UserUpdateCommand
|
||||
{
|
||||
public Guid UserId { get; private set; }
|
||||
public string? NickName { get; private set; }
|
||||
public string? Region { get; private set; }
|
||||
public string? Avatar { get; private set; }
|
||||
public string? Description { get; private set; }
|
||||
|
||||
public UserUpdateCommand(Guid userId, string? nickName, string? region, string? avatar, string? description)
|
||||
{
|
||||
UserId = userId;
|
||||
NickName = nickName;
|
||||
Region = region;
|
||||
Avatar = avatar;
|
||||
Description = description;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using IdentityService.Domain;
|
||||
using IdentityService.WebApi.Applications.Auth;
|
||||
using IdentityService.WebApi.Applications.Dtos;
|
||||
using IdentityService.WebApi.Applications.Dtos.Common;
|
||||
using IM.Commons;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace IdentityService.WebApi.Controllers.Auth
|
||||
{
|
||||
[Route("/api/[controller]/[action]")]
|
||||
[ApiController]
|
||||
public class AuthController : ControllerBase
|
||||
{
|
||||
private readonly AuthService authService;
|
||||
private readonly IIdRepository idRepository;
|
||||
|
||||
public AuthController(AuthService authService, IIdRepository idRepository)
|
||||
{
|
||||
this.authService = authService;
|
||||
this.idRepository = idRepository;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ProducesDefaultResponseType(typeof(Result<LoginResponse>))]
|
||||
public async Task<IActionResult> Login([FromBody] LoginRequest loginRequest)
|
||||
{
|
||||
return Ok(await authService.LoginAsync(loginRequest.UserName, loginRequest.Password));
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ProducesDefaultResponseType(typeof(Result<UserResponse>))]
|
||||
public async Task<IActionResult> Register([FromBody] RegisterRequest registerRequest)
|
||||
{
|
||||
return Ok(await authService.RegisterAsync(registerRequest.UserName, registerRequest.Password, registerRequest.NickName));
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ProducesDefaultResponseType(typeof(Result<LoginResponse>))]
|
||||
public async Task<IActionResult> Refresh([FromBody] RefreshRequest refreshRequest)
|
||||
{
|
||||
return Ok(await authService.RefreshAsync(refreshRequest.RefreshToken));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using FluentValidation;
|
||||
|
||||
namespace IdentityService.WebApi.Controllers.Auth
|
||||
{
|
||||
public class LoginRequest
|
||||
{
|
||||
public string UserName { get; set; }
|
||||
public string Password { get; set; }
|
||||
}
|
||||
|
||||
public class LoginRequestValidator : AbstractValidator<LoginRequest>
|
||||
{
|
||||
public LoginRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.UserName)
|
||||
.NotEmpty()
|
||||
.NotNull()
|
||||
.MaximumLength(20)
|
||||
.MinimumLength(5);
|
||||
|
||||
RuleFor(x => x.Password)
|
||||
.NotEmpty()
|
||||
.NotNull()
|
||||
.MaximumLength(50)
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using FluentValidation;
|
||||
|
||||
namespace IdentityService.WebApi.Controllers.Auth
|
||||
{
|
||||
public class RefreshRequest
|
||||
{
|
||||
public string RefreshToken { get; set; }
|
||||
}
|
||||
|
||||
public class RefreshTokenValidator : AbstractValidator<RefreshRequest>
|
||||
{
|
||||
public RefreshTokenValidator()
|
||||
{
|
||||
RuleFor(r => r.RefreshToken)
|
||||
.NotEmpty()
|
||||
.NotNull()
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using FluentValidation;
|
||||
|
||||
namespace IdentityService.WebApi.Controllers.Auth
|
||||
{
|
||||
public class RegisterRequest
|
||||
{
|
||||
public string UserName { get; set; }
|
||||
public string Password { get; set; }
|
||||
public string NickName { get; set; }
|
||||
}
|
||||
public class RegisterRequestValidator : AbstractValidator<RegisterRequest>
|
||||
{
|
||||
public RegisterRequestValidator()
|
||||
{
|
||||
RuleFor(r => r.UserName)
|
||||
.NotEmpty()
|
||||
.NotNull()
|
||||
.MaximumLength(20)
|
||||
.MinimumLength(5);
|
||||
|
||||
RuleFor(r => r.Password)
|
||||
.NotEmpty()
|
||||
.NotNull()
|
||||
.MinimumLength(6)
|
||||
.MaximumLength(50);
|
||||
|
||||
RuleFor(r => r.NickName)
|
||||
.NotEmpty()
|
||||
.NotNull()
|
||||
.MaximumLength(50);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using IdentityService.Infrastructure;
|
||||
using IdentityService.WebApi.Applications.Dtos.Common;
|
||||
using IdentityService.WebApi.Applications.User;
|
||||
using IM.ASPNETCore;
|
||||
using IM.Commons;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace IdentityService.WebApi.Controllers.User
|
||||
{
|
||||
[Authorize]
|
||||
[UnitOfWork(typeof(UserDbContext))]
|
||||
[Route("api/[controller]/[action]")]
|
||||
[ApiController]
|
||||
public class UserController : ControllerBase
|
||||
{
|
||||
private readonly UserService userService;
|
||||
public UserController(UserService userService)
|
||||
{
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[ProducesDefaultResponseType(typeof(Result<UserResponse?>))]
|
||||
public async Task<IActionResult> Me()
|
||||
{
|
||||
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
var res = await userService.GetUserInfoAsync(Guid.Parse(userId));
|
||||
return Ok(res);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[ProducesDefaultResponseType(typeof(Result<UserResponse?>))]
|
||||
public async Task<IActionResult> Find(Guid userId)
|
||||
{
|
||||
return Ok(await userService.GetUserInfoAsync(userId));
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ProducesDefaultResponseType(typeof(Result<UserResponse>))]
|
||||
public async Task<IActionResult> Update([FromBody] UserUpdateRequest request)
|
||||
{
|
||||
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
var res = await userService.UpdateAsync(new UserUpdateCommand(Guid.Parse(userId), request.NickName, request.Region, request.Avatar, request.Description));
|
||||
return Ok(res);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ProducesDefaultResponseType(typeof(Result<List<UserResponse>>))]
|
||||
public async Task<IActionResult> GetUsersByIds([FromBody] List<Guid> ids)
|
||||
{
|
||||
return Ok(await userService.GetUsersByIdsAsync(ids));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using FluentValidation;
|
||||
|
||||
namespace IdentityService.WebApi.Controllers.User
|
||||
{
|
||||
public class UserUpdateRequest
|
||||
{
|
||||
public string? NickName { get; set; }
|
||||
public string? Region { get; set; }
|
||||
public string? Avatar { get; set; }
|
||||
public string? Description { get; set; }
|
||||
}
|
||||
|
||||
public class UserUpdateRequestValidator : AbstractValidator<UserUpdateRequest>
|
||||
{
|
||||
public UserUpdateRequestValidator()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using IdentityService.Infrastructure;
|
||||
using IM.InitCommon;
|
||||
using Microsoft.EntityFrameworkCore.Design;
|
||||
|
||||
namespace IdentityService.WebApi
|
||||
{
|
||||
public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<UserDbContext>
|
||||
{
|
||||
public UserDbContext CreateDbContext(string[] args)
|
||||
{
|
||||
// 1. 复用你写好的配置工厂,提取连接字符串
|
||||
var optionsBuilder = DbContextOptionsBuilderFactory.Create<UserDbContext>();
|
||||
|
||||
// 2. 🌟 关键补刀:把假的 Mediator 传进去,满足构造函数的要求!
|
||||
return new UserDbContext(optionsBuilder.Options, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
FROM swr.cn-north-4.myhuaweicloud.com/ddn-k8s/mcr.microsoft.com/dotnet/sdk:8.0 AS build
|
||||
WORKDIR /src
|
||||
|
||||
COPY IM_API_NEW.sln ./
|
||||
|
||||
COPY User.WebApi/IdentityService.WebApi.csproj User.WebApi/
|
||||
COPY User.Domain/IdentityService.Domain.csproj User.Domain/
|
||||
COPY User.Infrastructure/IdentityService.Infrastructure.csproj User.Infrastructure/
|
||||
COPY DomainCommons/IM.DomainCommons.csproj DomainCommons/
|
||||
COPY Infrastructure/IM.Infrastructure.csproj Infrastructure/
|
||||
COPY IM.ASPNETCore/IM.ASPNETCore.csproj IM.ASPNETCore/
|
||||
COPY IM.Commons/IM.Commons.csproj IM.Commons/
|
||||
COPY IM.InitCommon/IM.InitCommon.csproj IM.InitCommon/
|
||||
COPY IM.Jwt/IM.Jwt.csproj IM.Jwt/
|
||||
COPY IM.Protocols/IM.Protocols.csproj IM.Protocols/
|
||||
|
||||
RUN dotnet restore User.WebApi/IdentityService.WebApi.csproj
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN dotnet publish User.WebApi/IdentityService.WebApi.csproj \
|
||||
-c Release \
|
||||
-o /app/publish \
|
||||
--no-restore
|
||||
|
||||
FROM swr.cn-north-4.myhuaweicloud.com/ddn-k8s/mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
|
||||
WORKDIR /app
|
||||
|
||||
ENV ASPNETCORE_ENVIRONMENT=Production
|
||||
ENV ASPNETCORE_URLS=http://+:8080
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
COPY --from=build /app/publish .
|
||||
|
||||
ENTRYPOINT ["dotnet", "IdentityService.WebApi.dll"]
|
||||
@@ -0,0 +1,24 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoMapper" Version="16.1.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\IM.InitCommon\IM.InitCommon.csproj" />
|
||||
<ProjectReference Include="..\IM.Protocols\IM.Protocols.csproj" />
|
||||
<ProjectReference Include="..\User.Infrastructure\IdentityService.Infrastructure.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,15 @@
|
||||
using IdentityService.WebApi.Applications.Auth;
|
||||
using IdentityService.WebApi.Applications.User;
|
||||
using IM.Commons;
|
||||
|
||||
namespace IdentityService.WebApi
|
||||
{
|
||||
public class ModuleInit : IModuleInitializer
|
||||
{
|
||||
public void Initialize(IServiceCollection services)
|
||||
{
|
||||
services.AddScoped<AuthService>();
|
||||
services.AddScoped<UserService>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
|
||||
using IdentityService.Domain.Entities;
|
||||
using IdentityService.Infrastructure;
|
||||
using IM.InitCommon;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace IdentityService.WebApi
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
|
||||
builder.ConfigureDbConfiguration();
|
||||
builder.ConfigExtraServices();
|
||||
// 2. 🌟 微软 Identity 终极注册组合拳
|
||||
builder.Services.AddIdentityCore<User>(options =>
|
||||
{
|
||||
// 这里可以顺手配置一下密码规则,比如不需要大写字母、最小长度等
|
||||
options.Password.RequireLowercase = false;
|
||||
options.Password.RequireNonAlphanumeric = false;
|
||||
options.Password.RequireUppercase = false;
|
||||
options.Password.RequiredLength = 6;
|
||||
})
|
||||
.AddRoles<Role>()
|
||||
.AddEntityFrameworkStores<UserDbContext>() // 👈 灵魂所在:自动向 DI 注入 IUserStore<User> 等几十个底层接口!
|
||||
.AddUserManager<IdUserManager>() // 👈 告诉框架:不要用你默认的 UserManager,用我自定义的 IdUserManager!
|
||||
.AddRoleManager<RoleManager<Role>>()
|
||||
.AddDefaultTokenProviders(); // 👈 顺带注册生成验证码/重置密码Token的服务
|
||||
|
||||
|
||||
builder.Services.AddAllGrpcServer();
|
||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
}
|
||||
|
||||
app.UseAppDefault();
|
||||
|
||||
app.MapControllers();
|
||||
app.MapAllGrpcServer();
|
||||
|
||||
app.Run();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"dotnetRunMessages": true,
|
||||
"applicationUrl": "http://localhost:5176"
|
||||
},
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"dotnetRunMessages": true,
|
||||
"applicationUrl": "https://localhost:7210;http://localhost:5176"
|
||||
},
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
},
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:8830",
|
||||
"sslPort": 44347
|
||||
}
|
||||
},
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"iissettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:53561/",
|
||||
"sslPort": 44384
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
using Grpc.Core;
|
||||
using IdentityService.WebApi.Applications.User;
|
||||
using IM.Protocols.Grpc.User;
|
||||
|
||||
namespace IdentityService.WebApi.Services
|
||||
{
|
||||
public class UserService:UserInternal.UserInternalBase
|
||||
{
|
||||
private readonly Applications.User.UserService service;
|
||||
|
||||
public UserService(Applications.User.UserService service)
|
||||
{
|
||||
this.service = service;
|
||||
}
|
||||
public override async Task<UserResponse> GetUserInfoAsync(GetUserInfoRequest request, ServerCallContext context)
|
||||
{
|
||||
var res = await service.GetUserInfoAsync(Guid.Parse(request.UserId));
|
||||
if (!res.Succeeded)
|
||||
{
|
||||
throw new RpcException(new Status(StatusCode.NotFound, res.Message));
|
||||
}
|
||||
|
||||
return new UserResponse()
|
||||
{
|
||||
Avatar = res.Data.Avatar ?? "",
|
||||
CreationTime = res.Data.CreationTime.ToUniversalTime().ToTimestamp(),
|
||||
Deletion = res.Data.Deletion is null ? DateTime.MinValue.ToUniversalTime().ToTimestamp() : res.Data.Deletion.Value.ToUniversalTime().ToTimestamp(),
|
||||
Description = res.Data.Description,
|
||||
Email = res.Data.Email ?? "",
|
||||
Id = res.Data.Id.ToString(),
|
||||
NickName = res.Data.NickName,
|
||||
Phone = res.Data.Phone ?? "",
|
||||
Region = res.Data.Region,
|
||||
UserName = res.Data.UserName
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
@User.WebApi_HostAddress = http://localhost:5176
|
||||
|
||||
GET {{User.WebApi_HostAddress}}/weatherforecast/
|
||||
Accept: application/json
|
||||
|
||||
###
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
Reference in New Issue
Block a user