ref: project structure

This commit is contained in:
2026-08-19 23:25:08 +07:00
parent 05a93cfc09
commit a1fe31cf70
155 changed files with 3339 additions and 1007 deletions
+9
View File
@@ -0,0 +1,9 @@
namespace mws.backend.dotnet.application.Common;
public class NotFoundException(string message) : Exception(message);
public class ForbiddenException(string message) : Exception(message);
public class UnauthorizedException(string message) : Exception(message);
public class BadRequestException(string message) : Exception(message);
+15
View File
@@ -0,0 +1,15 @@
using mws.backend.dotnet.application.Common.Repositories;
namespace mws.backend.dotnet.application.Common;
public interface IUnitOfWork
{
IUserRepository Users { get; }
IProjectRepository Projects { get; }
ITaskRepository Tasks { get; }
IDocumentRepository Documents { get; }
IRoleRepository Roles { get; }
IMasterDataRepository MasterData { get; }
Task<int> SaveChangesAsync(CancellationToken ct = default);
}
+37
View File
@@ -0,0 +1,37 @@
using AutoMapper;
using mws.backend.dotnet.application.Documents;
using mws.backend.dotnet.application.Permissions;
using mws.backend.dotnet.application.Projects;
using mws.backend.dotnet.application.Tasks;
using mws.backend.dotnet.application.Users;
using mws.backend.dotnet.domain.Documents;
using mws.backend.dotnet.domain.MasterData;
using mws.backend.dotnet.domain.Roles;
using mws.backend.dotnet.domain.Tasks;
using mws.backend.dotnet.domain.Users;
namespace mws.backend.dotnet.application.Common;
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<mws.backend.dotnet.domain.Projects.Project, ProjectDto>()
.ForMember(d => d.CreatedByName, opt => opt.MapFrom(s => s.CreatedByUser == null ? null : s.CreatedByUser.DisplayName))
.ForMember(d => d.UpdatedByName, opt => opt.MapFrom(s => s.UpdatedByUser == null ? null : s.UpdatedByUser.DisplayName));
CreateMap<Role, RoleDto>();
CreateMap<MasterDataEntry, MasterData.MasterDataDto>();
CreateMap<RolePermission, PermissionEntryDto>();
CreateMap<User, UserDto>()
.ForMember(d => d.RoleId, opt => opt.MapFrom(s => s.UserRoles.Select(r => r.RoleId).FirstOrDefault()))
.ForMember(d => d.RoleName, opt => opt.MapFrom(s => string.Join(", ", s.UserRoles.Select(r => r.Role.Name))));
CreateMap<Document, DocumentDto>();
CreateMap<Document, DocumentNodeDto>()
.ForMember(d => d.Children, opt => opt.Ignore());
CreateMap<TaskItem, TaskDto>()
.ForMember(d => d.AssigneeName, opt => opt.MapFrom(s => s.Assignee == null ? null : s.Assignee.DisplayName));
CreateMap<TaskItem, RecentTaskDto>()
.ForMember(d => d.Status, opt => opt.MapFrom(s => s.Status.ToString()));
CreateMap<Document, RecentDocumentDto>();
}
}
+9
View File
@@ -0,0 +1,9 @@
namespace mws.backend.dotnet.application.Common;
public class PagedResult<T>
{
public List<T> Items { get; init; } = [];
public int TotalCount { get; init; }
public int Page { get; init; }
public int PageSize { get; init; }
}
@@ -0,0 +1,15 @@
using mws.backend.dotnet.domain.Documents;
namespace mws.backend.dotnet.application.Common.Repositories;
public interface IDocumentRepository : IRepository<Document>
{
Task<Document?> GetByIdAsync(Guid id, CancellationToken ct = default);
Task<List<Document>> GetTreeForProjectAsync(Guid projectId, CancellationToken ct = default);
Task<Document?> GetInProjectAsync(Guid parentId, Guid projectId, CancellationToken ct = default);
Task<Guid?> GetParentIdAsync(Guid documentId, CancellationToken ct = default);
Task<List<Document>> GetChildrenAsync(Guid parentId, CancellationToken ct = default);
Task<List<Document>> SearchAsync(List<Guid> projectIds, string? term, int take, CancellationToken ct = default);
Task<int> CountByTypeForProjectAsync(Guid projectId, DocumentType type, CancellationToken ct = default);
Task<List<Document>> GetRecentForProjectAsync(Guid projectId, DocumentType type, int take, CancellationToken ct = default);
}
@@ -0,0 +1,12 @@
using mws.backend.dotnet.application.Common;
using mws.backend.dotnet.domain.MasterData;
namespace mws.backend.dotnet.application.Common.Repositories;
public interface IMasterDataRepository : IRepository<MasterDataEntry>
{
Task<PagedResult<MasterDataEntry>> GetAllAsync(string? group, int page, int pageSize, CancellationToken ct = default);
Task<List<MasterDataEntry>> GetActiveByGroupAsync(string group, CancellationToken ct = default);
Task<MasterDataEntry?> GetByIdAsync(Guid id, CancellationToken ct = default);
Task<bool> ExistsAsync(string group, string value, Guid? excludeId, CancellationToken ct = default);
}
@@ -0,0 +1,29 @@
using mws.backend.dotnet.application.Common;
using mws.backend.dotnet.domain.Projects;
namespace mws.backend.dotnet.application.Common.Repositories;
public interface IProjectRepository : IRepository<Project>
{
Task<Project?> GetByIdAsync(Guid id, CancellationToken ct = default);
Task<Project?> GetForUserAsync(Guid userId, Guid projectId, CancellationToken ct = default);
Task<PagedResult<Project>> GetForUserAsync(Guid userId, int page, int pageSize, CancellationToken ct = default);
Task<List<Project>> SearchForUserAsync(Guid userId, string? term, CancellationToken ct = default);
Task<int> CountMembersAsync(Guid projectId, CancellationToken ct = default);
Task<bool> IsMemberAsync(Guid projectId, Guid userId, CancellationToken ct = default);
Task<MemberRole?> GetMemberRoleAsync(Guid projectId, Guid userId, CancellationToken ct = default);
Task<ProjectMember?> GetMemberAsync(Guid projectId, Guid userId, CancellationToken ct = default);
Task<ProjectMember?> GetMemberWithUserAsync(Guid projectId, Guid userId, CancellationToken ct = default);
Task<PagedResult<ProjectMember>> GetMembersWithUserAsync(Guid projectId, int page, int pageSize, CancellationToken ct = default);
Task<int> CountOwnersAsync(Guid projectId, CancellationToken ct = default);
Task<List<Guid>> GetProjectIdsForUserAsync(Guid userId, CancellationToken ct = default);
Task<List<Guid>> GetOwnedProjectIdsAsync(Guid userId, CancellationToken ct = default);
Task<List<Guid>> GetDocumentViewableProjectIdsAsync(Guid userId, CancellationToken ct = default);
void AddMember(ProjectMember member);
void RemoveMember(ProjectMember member);
Task<ProjectMemberPermission?> GetMemberPermissionAsync(Guid projectId, Guid userId, string screen, CancellationToken ct = default);
Task<Dictionary<Guid, ProjectMemberPermission>> GetMemberPermissionsAsync(Guid projectId, string screen, CancellationToken ct = default);
void AddMemberPermission(ProjectMemberPermission permission);
}
@@ -0,0 +1,7 @@
namespace mws.backend.dotnet.application.Common.Repositories;
public interface IRepository<in T> where T : class
{
void Add(T entity);
void Remove(T entity);
}
@@ -0,0 +1,16 @@
using mws.backend.dotnet.application.Common;
using mws.backend.dotnet.domain.Roles;
namespace mws.backend.dotnet.application.Common.Repositories;
public interface IRoleRepository : IRepository<Role>
{
Task<PagedResult<Role>> GetAllWithPermissionsAsync(int page, int pageSize, CancellationToken ct = default);
Task<Role?> GetByIdWithPermissionsAsync(Guid id, CancellationToken ct = default);
Task<Role?> GetByIdAsync(Guid id, CancellationToken ct = default);
Task<bool> ExistsByNameAsync(string name, Guid? excludeId, CancellationToken ct = default);
Task<RolePermission?> GetPermissionAsync(Guid roleId, string screen, CancellationToken ct = default);
Task<Dictionary<string, RolePermission>> GetPermissionsAsync(Guid roleId, CancellationToken ct = default);
Task<List<RolePermission>> GetPermissionsForRolesAsync(IEnumerable<Guid> roleIds, CancellationToken ct = default);
void RemovePermissions(IEnumerable<RolePermission> permissions);
}
@@ -0,0 +1,17 @@
using mws.backend.dotnet.application.Common;
using mws.backend.dotnet.domain.Tasks;
using TaskPriority = mws.backend.dotnet.domain.Tasks.TaskPriority;
using TaskStatus = mws.backend.dotnet.domain.Tasks.TaskStatus;
namespace mws.backend.dotnet.application.Common.Repositories;
public interface ITaskRepository : IRepository<TaskItem>
{
Task<TaskItem?> GetByIdAsync(Guid id, CancellationToken ct = default);
Task<TaskItem?> GetWithAssigneeAsync(Guid id, CancellationToken ct = default);
Task<PagedResult<TaskItem>> GetForProjectAsync(
Guid projectId, TaskStatus? status, TaskPriority? priority, Guid? assigneeId, int page, int pageSize, CancellationToken ct = default);
Task<List<TaskItem>> SearchWithAssigneeAsync(List<Guid> projectIds, string? term, int take, CancellationToken ct = default);
Task<Dictionary<TaskStatus, int>> CountByStatusForProjectAsync(Guid projectId, CancellationToken ct = default);
Task<List<TaskItem>> GetRecentForProjectAsync(Guid projectId, int take, CancellationToken ct = default);
}
@@ -0,0 +1,16 @@
using mws.backend.dotnet.application.Common;
using mws.backend.dotnet.domain.Users;
namespace mws.backend.dotnet.application.Common.Repositories;
public interface IUserRepository : IRepository<User>
{
Task<User?> GetByIdAsync(Guid id, CancellationToken ct = default);
Task<User?> GetByIdWithRoleAsync(Guid id, CancellationToken ct = default);
Task<User?> GetByUsernameWithRoleAsync(string username, CancellationToken ct = default);
Task<bool> ExistsByUsernameAsync(string username, CancellationToken ct = default);
Task<bool> ExistsByRoleIdAsync(Guid roleId, CancellationToken ct = default);
Task<List<Guid>> GetRoleIdsAsync(Guid userId, CancellationToken ct = default);
Task<List<User>> SearchWithRoleAsync(string? term, int? take, CancellationToken ct = default);
Task<PagedResult<User>> SearchWithRolePagedAsync(string? term, int page, int pageSize, CancellationToken ct = default);
}