添加项目文件。

This commit is contained in:
2026-05-09 17:06:30 +08:00
parent c60f5fe117
commit 720ef957d4
378 changed files with 14843 additions and 0 deletions
+13
View File
@@ -0,0 +1,13 @@
using MassTransit;
using Microsoft.AspNetCore.Identity;
namespace IdentityService.Domain.Entities
{
public class Role : IdentityRole<Guid>
{
public Role()
{
Id = NewId.NextGuid();
}
}
}
+130
View File
@@ -0,0 +1,130 @@
using IdentityService.Domain.Events;
using IM.DomainCommons;
using MassTransit;
using MediatR;
using Microsoft.AspNetCore.Identity;
using System.ComponentModel.DataAnnotations.Schema;
namespace IdentityService.Domain.Entities
{
public class User : IdentityUser<Guid>, IHasCreationTime, IHasDeletionTime, ISoftDelete, IDomainEvents, IHasModificationTime
{
[NotMapped]
public List<INotification> domainEvents = [];
/// <summary>
/// 用户昵称
/// </summary>
public string NickName { get; private set; } = null!;
/// <summary>
/// 用户签名
/// </summary>
public string Description { get; private set; } = "";
/// <summary>
/// 地区
/// </summary>
public string Region { get; private set; } = "未知地区";
/// <summary>
/// 用户在线状态
/// 0(默认):不在线
/// 1:在线
/// </summary>
//public UserOnlineState OnlineStatus { get; private set; }
/// <summary>
/// 账户状态
/// (0:未激活,1:正常,2:封禁)
/// </summary>
public UserState Status { get; private set; }
/// <summary>
/// 用户头像链接
/// </summary>
public string? Avatar { get; private set; }
public DateTimeOffset CreationTime { get; private set; } = DateTime.Now;
public DateTimeOffset? Deletion { get; private set; }
public bool IsDeleted { get; private set; }
public DateTimeOffset? ModificationTime { get; private set; }
private User() { }
public User(string username, string nickName)
{
Id = NewId.NextGuid();
UserName = username;
NickName = nickName;
}
public void Ban(string reason)
{
if (this.Status == UserState.Banned) return;
this.Status = UserState.Banned;
AddDomainEvent(new UserBannedDomainEvent(this.Id, reason));
}
public void Update(string? nickName, string? region, string? avatar, string? desc)
{
if (nickName != null)
{
if (nickName.Trim() == string.Empty) throw new DomainException("昵称不可为空");
if (nickName.Length > 20) throw new DomainException("昵称不可大于20");
this.NickName = nickName;
}
if (region != null)
{
if (region.Trim() == string.Empty) throw new DomainException("地区不可为空");
if (region.Length > 20) throw new DomainException("地区不可大于20");
this.Region = region;
}
if (avatar != null)
{
this.Avatar = avatar;
}
if (desc != null)
{
this.Description = desc;
}
ModificationTime = DateTime.Now;
AddDomainEvent(new UserProfileUpdateDomainEvent(this));
}
public void SoftDelete()
{
this.IsDeleted = true;
}
public IEnumerable<INotification> GetDomainEvents()
{
return domainEvents;
}
public void AddDomainEvent(INotification eventItem)
{
domainEvents.Add(eventItem);
}
public void AddDomainEventIfAbsent(INotification eventItem)
{
if (!domainEvents.Contains(eventItem))
{
domainEvents.Add(eventItem);
}
}
public void ClearDomainEvents()
{
domainEvents.Clear();
}
}
}
@@ -0,0 +1,6 @@
using MediatR;
namespace IdentityService.Domain.Events
{
public record UserBannedDomainEvent(Guid UserId, string Reason) : INotification;
}
@@ -0,0 +1,7 @@
using IdentityService.Domain.Entities;
using MediatR;
namespace IdentityService.Domain.Events
{
public record UserProfileUpdateDomainEvent(User User) : INotification;
}
+115
View File
@@ -0,0 +1,115 @@
using IdentityService.Domain.Entities;
using IM.Commons;
using Microsoft.AspNetCore.Identity;
namespace IdentityService.Domain
{
public interface IIdRepository
{
/// <summary>
/// 通过用户名查找
/// </summary>
/// <param name="userName">用户名</param>
/// <returns></returns>
Task<User?> FindByUserNameAsync(string userName);
/// <summary>
/// 通过邮箱地址查找
/// </summary>
/// <param name="Email">邮箱地址</param>
/// <returns></returns>
Task<User?> FindByEmailAsync(string Email);
/// <summary>
/// 通过ID查询
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
Task<User?> FindByIdAsync(Guid id);
/// <summary>
/// 通过手机号查找
/// </summary>
/// <param name="phone"></param>
/// <returns></returns>
Task<User?> FindByPhoneAsync(string phone);
/// <summary>
/// 创建用户
/// </summary>
/// <param name="user"></param>
/// <param name="password"></param>
/// <returns></returns>
Task<IdentityResult> CreateAsync(User user, string password);
/// <summary>
/// 修改密码
/// </summary>
/// <param name="userId"></param>
/// <param name="password">原密码</param>
/// <returns></returns>
Task<IdentityResult> ChangePasswordAsync(User user, string password);
/// <summary>
/// 记录一次失败登录
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
Task<IdentityResult> AccessFailedAsync(User user);
/// <summary>
/// 获取角色
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
Task<IList<string>> GetRolesAsync(User user);
/// <summary>
/// 添加角色
/// </summary>
/// <param name="userId"></param>
/// <param name="role"></param>
/// <returns></returns>
Task<IdentityResult> AddRoleAsync(User user, string role);
/// <summary>
/// 为登录检查用户名密码
/// </summary>
/// <param name="userName"></param>
/// <param name="password"></param>
/// <param name="lockoutOnFailure">是否记录登录失败</param>
/// <returns></returns>
Task<SignInResult> CheckForSignInAsync(User user, string password, bool lockoutOnFailure);
/// <summary>
/// 删除用户
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
Task<IdentityResult> RemoveAsync(User user);
/// <summary>
/// 重置密码
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
Task<(IdentityResult, User?, string password)> ResetPasswordAsync(User user);
/// <summary>
/// 检查用户名
/// </summary>
/// <param name="username"></param>
/// <returns></returns>
Task<SignInResult> CheckUsernameAsync(string username);
/// <summary>
/// 检查邮箱
/// </summary>
/// <param name="email"></param>
/// <returns></returns>
Task<SignInResult> CheckEmailAsync(string email);
/// <summary>
/// 检查手机号
/// </summary>
/// <param name="phone"></param>
/// <returns></returns>
Task<SignInResult> CheckPhoneAsync(string phone);
/// <summary>
/// 批量获取用户信息
/// </summary>
/// <param name="ids"></param>
/// <returns></returns>
Task<IEnumerable<TResult>> GetUsersAsync<TResult>(ISpecification<User, TResult> specification);
Task<IEnumerable<User>> GetUsersAsync(ISpecification<User> specification);
}
}
+26
View File
@@ -0,0 +1,26 @@
using IdentityService.Domain.Entities;
using IM.Commons;
using Microsoft.AspNetCore.Identity;
namespace IdentityService.Domain
{
public class IdDomainService
{
private readonly IIdRepository idRepository;
public IdDomainService(IIdRepository idRepository)
{
this.idRepository = idRepository;
}
public async Task<Result<User?>> CreateUserAsync(string userName, string password, string nickName)
{
SignInResult checkUsername = await idRepository.CheckUsernameAsync(userName);
if (!checkUsername.Succeeded)
{
return Result<User?>.Fail(ResultCode.USER_ALREADY_EXISTS);
}
return Result<User?>.Success(new User(userName, nickName));
}
}
}
+18
View File
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="10.0.5" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\IM.Commons\IM.Commons.csproj" />
<ProjectReference Include="..\DomainCommons\IM.DomainCommons.csproj" />
</ItemGroup>
</Project>
+8
View File
@@ -0,0 +1,8 @@
namespace IdentityService.Domain
{
public enum UserOnlineState
{
Offline = 0,
Online = 1,
}
}
+10
View File
@@ -0,0 +1,10 @@
namespace IdentityService.Domain
{
public enum UserState
{
Inactive = 0,
Normal = 1,
Banned = 2
}
}
+32
View File
@@ -0,0 +1,32 @@
using System.Text.RegularExpressions;
namespace IdentityService.Domain.ValueObjects
{
public class Email
{
public string Value { get; }
public Email(string value)
{
if (string.IsNullOrWhiteSpace(value))
{
throw new ArgumentNullException("邮箱地址不可为空");
}
string pattern = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$";
if (!Regex.IsMatch(value, pattern))
{
throw new ArgumentException("邮箱地址格式不符合要求");
}
this.Value = value.ToLowerInvariant();
}
public override string ToString()
{
return Value;
}
public static implicit operator Email(string value)
{
return new Email(value);
}
}
}
+33
View File
@@ -0,0 +1,33 @@
using System.Text.RegularExpressions;
namespace IdentityService.Domain.ValueObjects
{
public class Phone
{
public string Value { get; }
public Phone(string value)
{
if (string.IsNullOrWhiteSpace(value))
{
throw new ArgumentNullException("手机号不可为空");
}
string pattern = "^1[3-9]\\d{9}$";
if (!Regex.IsMatch(value, pattern))
{
throw new ArgumentException("手机号格式不符合要求");
}
Value = value;
}
public override string ToString()
{
return Value;
}
public static implicit operator Phone(string value)
{
return new Phone(value);
}
}
}