Merge branch 'development'

This commit is contained in:
2024-03-08 11:31:56 +08:00
226 changed files with 5698 additions and 1610 deletions

View File

@@ -17,8 +17,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Kurisu.AspNetCore" Version="0.5.2" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.4" />
<PackageReference Include="Kurisu.AspNetCore" Version="0.6.0" />
</ItemGroup>
<ItemGroup>

View File

@@ -10,7 +10,7 @@ namespace Dlhis.Sys.Identity.Controllers;
/// <summary>
/// 角色
/// </summary>
[Authorize(Roles = "admin")]
[Authorize]
[Route("api/[controller]")]
[ApiController]
public class RolesController : ControllerBase
@@ -27,9 +27,66 @@ public class RolesController : ControllerBase
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[HttpGet]
public async Task<Pagination<RoleRespone>> GetPageAsync(RolePageRequest request)
[Authorize(Roles = "admin")]
[HttpGet("page")]
public async Task<Pagination<RoleRespone>> GetPageAsync([FromQuery] RolePageRequest request)
{
return await _roleService.GetPageAsync(request);
}
/// <summary>
/// 角色列表
/// </summary>
/// <returns></returns>
[Authorize(Roles = "admin")]
[HttpGet("list")]
public async Task<List<RoleRespone>> GetListAsync()
{
return await _roleService.GetListAsync();
}
/// <summary>
/// 分配角色
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[Authorize(Roles = "admin")]
[HttpPost("assign")]
public async Task AssignRole(AssignRoleRequest request)
{
await _roleService.AssignRoleAsync(request);
}
/// <summary>
/// 保存角色
/// </summary>
/// <returns></returns>
[Authorize(Roles = "admin")]
[HttpPost("save")]
public async Task SaveRole(RoleSaveRequest request)
{
await _roleService.SaveRoleAsync(request);
}
/// <summary>
/// 移除角色
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[Authorize(Roles = "admin")]
[HttpPost("delete")]
public async Task RemoveRole(RoleRemoveRequest request)
{
await _roleService.RemoveRoleAsync(request);
}
/// <summary>
/// 我的角色
/// </summary>
/// <returns></returns>
[HttpGet("my")]
public async Task<RoleRespone> GetMyRoleAsync(int? userId)
{
return await _roleService.GetMyRoleAsync(userId);
}
}

View File

@@ -51,6 +51,16 @@ public class TenantsController : ControllerBase
return await _tenantService.GetListAsync();
}
/// <summary>
/// 租户树
/// </summary>
/// <returns></returns>
[HttpGet("tree")]
public async Task<List<TenantTreeResponse>> GetTreeAsync()
{
return await _tenantService.GetTreeAsync();
}
/// <summary>
/// 获取租户
/// </summary>
@@ -61,6 +71,17 @@ public class TenantsController : ControllerBase
return await _tenantService.GetTenantByCode(code);
}
/// <summary>
/// 保存租户
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[HttpPost("save")]
public async Task SaveTenantAsync(TenantSaveRequest request)
{
await _tenantService.SaveTenantAsync(request);
}
/// <summary>
/// 获取我的管理租户
/// </summary>

View File

@@ -0,0 +1,8 @@
namespace Dlhis.Sys.Identity.Request;
public class AssignRoleRequest
{
public int UserId { get; set; }
public int? RoleId { get; set; }
}

View File

@@ -0,0 +1,6 @@
namespace Dlhis.Sys.Identity.Request;
public class RoleRemoveRequest
{
public int Id { get; set; }
}

View File

@@ -0,0 +1,11 @@
using System.ComponentModel.DataAnnotations;
namespace Dlhis.Sys.Identity.Request;
public class RoleSaveRequest
{
public int? Id { get; set; }
[Required(ErrorMessage = "角色名不能为空")]
public string Name { get; set; }
}

View File

@@ -0,0 +1,25 @@
using System.ComponentModel.DataAnnotations;
namespace Dlhis.Sys.Identity.Request;
public class TenantSaveRequest
{
public int? Id { get; set; }
/// <summary>
/// 上级
/// </summary>
public string PCode { get; set; }
/// <summary>
/// 代码
/// </summary>
[Required(ErrorMessage = "基地/租户代码不能为空")]
public string Code { get; set; }
/// <summary>
/// 名称
/// </summary>
[Required(ErrorMessage = "基地/租户名称不能为空")]
public string Name { get; set; }
}

View File

@@ -7,4 +7,9 @@ public class RoleRespone
public string Name { get; set; }
public string NormalizedName { get; set; }
/// <summary>
/// 是否可编辑
/// </summary>
public bool Editable => Id > 5;
}

View File

@@ -4,6 +4,8 @@ public class TenantResponse
{
public int Id { get; set; }
public string PCode { get; set; }
public string Code { get; set; }
public string Name { get; set; }

View File

@@ -0,0 +1,12 @@
using Newtonsoft.Json;
namespace Dlhis.Sys.Identity.Response;
public class TenantTreeResponse : TenantResponse
{
/// <summary>
/// 下级
/// </summary>
[JsonProperty(Order = 99)]
public List<TenantTreeResponse> Next { get; set; }
}

View File

@@ -4,25 +4,64 @@ public class UserResponse
{
public int Id { get; set; }
/// <summary>
/// 账号名称
/// </summary>
public string UserName { get; set; }
/// <summary>
/// 规范化账号名称
/// </summary>
public string NormalizedUserName { get; set; }
/// <summary>
/// 邮件
/// </summary>
public string Email { get; set; }
/// <summary>
/// 规范化邮件
/// </summary>
public string NormalizedEmail { get; set; }
/// <summary>
/// 邮件是否已确认
/// </summary>
public bool EmailConfirmed { get; set; }
/// <summary>
/// 手机号
/// </summary>
public string PhoneNumber { get; set; }
/// <summary>
/// 手机号是否已确认
/// </summary>
public bool PhoneNumberConfirmed { get; set; }
/// <summary>
/// 是否开启双因素认证
/// </summary>
public bool TwoFactorEnabled { get; set; }
/// <summary>
/// 账号锁定结束时间
/// </summary>
public DateTime? LockoutEnd { get; set; }
/// <summary>
/// 是否开启账号锁定功能
/// </summary>
public bool LockoutEnabled { get; set; }
/// <summary>
/// 登陆失败次数
/// </summary>
public int AccessFailedCount { get; set; }
/// <summary>
/// 角色
/// </summary>
public string Role { get; set; }
}

View File

@@ -7,4 +7,14 @@ namespace Dlhis.User.Identity.Services;
public interface IRoleService : IScopeDependency
{
Task<Pagination<RoleRespone>> GetPageAsync(RolePageRequest request);
Task<List<RoleRespone>> GetListAsync();
Task SaveRoleAsync(RoleSaveRequest request);
Task<RoleRespone> GetMyRoleAsync(int? userId);
Task RemoveRoleAsync(RoleRemoveRequest request);
Task AssignRoleAsync(AssignRoleRequest request);
}

View File

@@ -10,7 +10,11 @@ public interface ITenantService : IScopeDependency
Task<List<TenantResponse>> GetListAsync();
Task<List<TenantTreeResponse>> GetTreeAsync();
Task<TenantResponse> GetTenantByCode(string code);
Task<List<TenantResponse>> GetMyTenantsAsync();
Task SaveTenantAsync(TenantSaveRequest request);
}

View File

@@ -2,8 +2,11 @@
using Dlhis.Sys.Identity.Request;
using Dlhis.Sys.Identity.Response;
using Dlhis.User.Identity.Services;
using Kurisu.AspNetCore.Scope;
using Kurisu.AspNetCore.UnifyResultAndValidation;
using Kurisu.AspNetCore.Utils.Extensions;
using Kurisu.Core.DataAccess.Dto;
using Kurisu.Core.User.Abstractions;
using Kurisu.SqlSugar.Extensions;
using Kurisu.SqlSugar.Services;
@@ -11,20 +14,176 @@ namespace Dlhis.Sys.Identity.Services.Implements;
public class RoleService : IRoleService
{
private readonly IDbContext _dbContext;
private readonly IConfiguration _configuration;
private readonly ICurrentUser _currentUser;
public RoleService(IDbContext dbContext, IConfiguration configuration)
public RoleService(IConfiguration configuration, ICurrentUser currentUser)
{
var identityConnection = configuration.GetSection("SqlSugarOptions:IdentityConnectionString").Value;
dbContext.Client.CurrentConnectionConfig.ConnectionString = identityConnection;
_dbContext = dbContext;
_configuration = configuration;
_currentUser = currentUser;
}
public async Task<Pagination<RoleRespone>> GetPageAsync(RolePageRequest request)
{
return await _dbContext.Queryable<RoleEntity>()
return await Scoped.Request.Value.CreateAsync(async sp =>
{
var dbContext = sp.GetService<IDbContext>();
var identityConnection = _configuration.GetSection("SqlSugarOptions:IdentityConnectionString").Value;
dbContext.Client.CurrentConnectionConfig.ConnectionString = identityConnection;
return await dbContext.Queryable<RoleEntity>()
.WhereIF(request.Search.IsPresent(), x => x.Name.Contains(request.Search) || x.NormalizedName.Contains(request.Search))
.Select<RoleRespone>()
.ToPageListAsync(request);
});
}
public async Task<RoleRespone> GetMyRoleAsync(int? userId)
{
userId ??= _currentUser.GetIntSubjectId();
return await Scoped.Request.Value.CreateAsync(async sp =>
{
var dbContext = sp.GetService<IDbContext>();
var identityConnection = _configuration.GetSection("SqlSugarOptions:IdentityConnectionString").Value;
dbContext.Client.CurrentConnectionConfig.ConnectionString = identityConnection;
return await dbContext.Queryable<UserRoleEntity>()
.LeftJoin<RoleEntity>((x, r) => x.RoleId == r.Id)
.Where(x => x.UserId == userId)
.Select((x, r) => new RoleRespone
{
Id = r.Id,
Name = r.Name,
NormalizedName = r.NormalizedName,
})
.FirstAsync();
});
}
public async Task SaveRoleAsync(RoleSaveRequest request)
{
await Scoped.Request.Value.CreateAsync(async sp =>
{
var dbContext = sp.GetService<IDbContext>();
var identityConnection = _configuration.GetSection("SqlSugarOptions:IdentityConnectionString").Value;
dbContext.Client.CurrentConnectionConfig.ConnectionString = identityConnection;
await CheckDuplicateNameAsync(dbContext, request);
if (!request.Id.HasValue)
{
await dbContext.InsertAsync(new RoleEntity
{
Name = request.Name,
NormalizedName = request.Name.ToUpperInvariant(),
ConcurrencyStamp = Guid.NewGuid(),
});
}
else
{
var entity = await dbContext.Queryable<RoleEntity>().Where(x => x.Id == request.Id).FirstAsync();
entity.ThrowIfNull("角色不存在");
entity.Name = request.Name;
entity.NormalizedName = request.Name.ToUpperInvariant();
entity.ConcurrencyStamp = Guid.NewGuid();
await dbContext.UpdateAsync(entity);
}
});
}
private async Task CheckDuplicateNameAsync(IDbContext db, RoleSaveRequest request)
{
var exists = await db.Queryable<RoleEntity>()
.WhereIF(request.Id.HasValue, x => x.Id != request.Id)
.Where(x => x.Name == request.Name)
.AnyAsync();
exists.ThrowIfTrue($"重复角色名称{request.Name}");
}
public async Task AssignRoleAsync(AssignRoleRequest request)
{
var myUserId = _currentUser.GetIntSubjectId();
await Scoped.Request.Value.CreateAsync(async sp =>
{
//不能被其他人修改
if (request.UserId == 1 && request.UserId != myUserId)
{
throw new UserFriendlyException("无法修改admin角色");
}
var dbContext = sp.GetService<IDbContext>();
var identityConnection = _configuration.GetSection("SqlSugarOptions:IdentityConnectionString").Value;
dbContext.Client.CurrentConnectionConfig.ConnectionString = identityConnection;
var existsUser = await dbContext.Queryable<UserEntity>().Where(x => x.Id == request.UserId).AnyAsync();
existsUser.ThrowIfFalse("分配的用户不存在");
var userRole = await dbContext.Queryable<UserRoleEntity>().Where(x => x.UserId == request.UserId).FirstAsync();
if (request.RoleId.HasValue)
{
var existsRole = await dbContext.Queryable<RoleEntity>().Where(x => x.Id == request.RoleId).AnyAsync();
existsUser.ThrowIfFalse("分配的角色不存在");
if (userRole == null)
{
await dbContext.InsertAsync(new UserRoleEntity
{
UserId = request.UserId,
RoleId = request.RoleId.Value,
});
}
else
{
await dbContext.Updateable<UserRoleEntity>()
.SetColumns(x => x.RoleId == request.RoleId)
.Where(x => x.UserId == request.UserId)
.ExecuteCommandAsync();
}
}
else
{
await dbContext.DeleteReallyAsync(userRole);
}
});
}
public async Task RemoveRoleAsync(RoleRemoveRequest request)
{
await Scoped.Request.Value.CreateAsync(async sp =>
{
var dbContext = sp.GetService<IDbContext>();
var identityConnection = _configuration.GetSection("SqlSugarOptions:IdentityConnectionString").Value;
dbContext.Client.CurrentConnectionConfig.ConnectionString = identityConnection;
var role = await dbContext.Queryable<RoleEntity>().FirstAsync(x => x.Id == request.Id);
role.ThrowIfNull("角色不存在");
//删除角色,自动删除UserRoles对应关系
await dbContext.DeleteReallyAsync(role);
});
}
public async Task<List<RoleRespone>> GetListAsync()
{
return await Scoped.Request.Value.CreateAsync(async sp =>
{
var dbContext = sp.GetService<IDbContext>();
var identityConnection = _configuration.GetSection("SqlSugarOptions:IdentityConnectionString").Value;
dbContext.Client.CurrentConnectionConfig.ConnectionString = identityConnection;
return await dbContext.Queryable<RoleEntity>()
.Select<RoleRespone>()
.ToListAsync();
});
}
}

View File

@@ -14,11 +14,9 @@ public class TenantService : ITenantService
private readonly IDbContext _dbContext;
private readonly ICurrentUser _currentUser;
public TenantService(IDbContext dbContext, IConfiguration configuration, ICurrentUser currentUser)
public TenantService(IDbContext dbContext, ICurrentUser currentUser)
{
var identityConnection = configuration.GetSection("SqlSugarOptions:IdentityConnectionString").Value;
dbContext.Client.CurrentConnectionConfig.ConnectionString = identityConnection;
_dbContext = dbContext;
_dbContext = dbContext.ChangeDb("identity");
_currentUser = currentUser;
}
@@ -45,25 +43,77 @@ public class TenantService : ITenantService
.FirstAsync();
}
public async Task SaveTenantAsync(TenantSaveRequest request)
{
if (!request.Id.HasValue)
{
await _dbContext.InsertAsync(new TenantEntity
{
PCode = request.PCode,
Code = request.Code,
Name = request.Name,
});
}
else
{
var entity = await _dbContext.Queryable<TenantEntity>().FirstAsync(x => x.Id == request.Id);
entity.ThrowIfNull("基地/租户信息不存在");
entity.Code = request.Code;
entity.Name = request.Name;
await _dbContext.UpdateAsync(entity);
}
}
public async Task<List<TenantResponse>> GetMyTenantsAsync()
{
var userId = _currentUser.GetIntSubjectId();
var tenantId = _currentUser.GetStringTenantId();
var myTenants = await _dbContext.Queryable<UserTenantEntity>()
.LeftJoin<TenantEntity>((ut, t) => ut.TenantCode == t.Code)
.Where(ut => ut.UserId == userId)
.Select((ut, t) => new TenantResponse
{
Id = t.Id,
PCode = t.PCode,
Code = t.Code,
Name = t.Name,
})
.ToListAsync();
if (myTenants.IsEmpty())
{
myTenants = await GetListAsync();
}
var currentTenant = myTenants.First(x => x.Code == tenantId);
var otherTenants = myTenants.Where(x => x.Code != tenantId);
return myTenants;
var result = new List<TenantResponse> { currentTenant };
result.AddRange(otherTenants);
return result;
}
public async Task<List<TenantTreeResponse>> GetTreeAsync()
{
var tenants = await _dbContext.Queryable<TenantEntity>()
.Select<TenantTreeResponse>()
.ToListAsync();
var tops = tenants.Where(x => x.PCode.IsEmpty()).ToList();
foreach (var item in tops)
{
RecursionGetNext(item, tenants);
}
return tops;
}
private void RecursionGetNext(TenantTreeResponse current, List<TenantTreeResponse> data)
{
var next = data.Where(x => x.PCode == current.Code).ToList();
current.Next = next;
foreach (var item in next)
{
RecursionGetNext(item, data);
}
}
}

View File

@@ -1,5 +1,4 @@
using System.Configuration;
using Dlhis.Entity.Sys.Entities;
using Dlhis.Entity.Sys.Entities;
using Dlhis.Sys.Identity.Request;
using Dlhis.Sys.Identity.Response;
using Kurisu.AspNetCore.Utils.Extensions;
@@ -23,13 +22,19 @@ public class UserService : IUserService
public async Task<Pagination<UserResponse>> GetPageAsync(UserPageRequest request)
{
return await _dbContext.Queryable<UserEntity>()
.LeftJoin<UserRoleEntity>((x, ur) => x.Id == ur.UserId)
.LeftJoin<RoleEntity>((x, ur, r) => ur.RoleId == r.Id)
.WhereIF(request.Search.IsPresent(), x => x.UserName.Contains(request.Search)
|| x.NormalizedUserName.Contains(request.Search)
|| x.Email.Contains(request.Search)
|| x.NormalizedEmail.Contains(request.Search)
|| x.PhoneNumber.Contains(request.Search)
)
.Select<UserResponse>()
.Select((x, ur, r) => new UserResponse
{
Id = x.Id,
Role = r.Name
}, true)
.ToPageListAsync(request);
}
@@ -44,7 +49,6 @@ public class UserService : IUserService
public async Task<List<UserResponse>> GetUsersAsync()
{
//todo tenant
var users = await _dbContext.Queryable<UserEntity>().Select<UserResponse>().ToListAsync();
return users;
}

View File

@@ -5,6 +5,7 @@ using Dlhis.Sys.Menu.Services;
using Dlhis.Sys.Menu.Response;
using Dlhis.Sys.Menu.Request;
using Dlhis.Entity.Sys.Enums;
using Kurisu.AspNetCore.DataAccess.SqlSugar.Aop;
namespace Dlhis.Sys.Menu.Controllers;
@@ -40,6 +41,7 @@ public class MenusController : ControllerBase
/// <param name="request"></param>
/// <returns></returns>
[HttpPost]
[Transactional]
public async Task Save(SaveMenuRequest request)
{
await _menuService.SaveMenuAsync(request);

View File

@@ -41,7 +41,7 @@ public class MenuService : IMenuService
return top;
}
[Transactional]
public async Task<string> SaveMenuAsync(SaveMenuRequest request)
{
await CheckAsync(request);

View File

@@ -1,5 +1,6 @@
using Kurisu.AspNetCore.Startup;
using Kurisu.SqlSugar.Extensions;
using SqlSugar;
namespace Dlhis.Sys;
@@ -9,6 +10,27 @@ public class SqlSugarPack : BaseAppPack
public override void ConfigureServices(IServiceCollection services)
{
services.AddSqlSugar();
services.AddSqlSugar(sp =>
{
var configuration = sp.GetService<IConfiguration>();
return new List<ConnectionConfig>
{
new ConnectionConfig
{
ConfigId = "identity",
ConnectionString = configuration.GetSection("SqlSugarOptions:IdentityConnectionString").Value,
DbType = DbType.MySql,
InitKeyType = InitKeyType.Attribute,
IsAutoCloseConnection = true,
MoreSettings = new ConnMoreSettings
{
DisableNvarchar = true,
}
}
};
});
}
}
}