improvement: 마이페이지 조회 확장 (#249) #250
|
|
@ -53,4 +53,20 @@ public class ProfileController : ControllerBase
|
|||
var result = await _authService.UpdateProfileAsync(adminId, request);
|
||||
return Ok(ApiResponse<ProfileResponseDto>.Success(result));
|
||||
}
|
||||
|
||||
[HttpPost("activity/list")]
|
||||
[SwaggerOperation(
|
||||
Summary = "활동 내역 조회",
|
||||
Description = "현재 로그인된 관리자의 활동 내역을 페이징 조회합니다.")]
|
||||
[SwaggerResponse(200, "조회 성공", typeof(ApiResponse<ActivityListResponseDto>))]
|
||||
[SwaggerResponse(401, "인증되지 않은 요청")]
|
||||
public async Task<IActionResult> GetActivityListAsync([FromBody] ActivityListRequestDto request)
|
||||
{
|
||||
var adminIdClaim = User.FindFirst("adminId")?.Value;
|
||||
if (string.IsNullOrEmpty(adminIdClaim) || !long.TryParse(adminIdClaim, out var adminId))
|
||||
throw SpmsException.Unauthorized("인증 정보가 유효하지 않습니다.");
|
||||
|
||||
var result = await _authService.GetActivityListAsync(adminId, request);
|
||||
return Ok(ApiResponse<ActivityListResponseDto>.Success(result));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
18
SPMS.Application/DTOs/Account/ActivityListRequestDto.cs
Normal file
18
SPMS.Application/DTOs/Account/ActivityListRequestDto.cs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace SPMS.Application.DTOs.Account;
|
||||
|
||||
public class ActivityListRequestDto
|
||||
{
|
||||
[JsonPropertyName("page")]
|
||||
public int Page { get; set; } = 1;
|
||||
|
||||
[JsonPropertyName("size")]
|
||||
public int Size { get; set; } = 10;
|
||||
|
||||
[JsonPropertyName("from")]
|
||||
public DateTime? From { get; set; }
|
||||
|
||||
[JsonPropertyName("to")]
|
||||
public DateTime? To { get; set; }
|
||||
}
|
||||
31
SPMS.Application/DTOs/Account/ActivityListResponseDto.cs
Normal file
31
SPMS.Application/DTOs/Account/ActivityListResponseDto.cs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
using System.Text.Json.Serialization;
|
||||
using SPMS.Application.DTOs.Notice;
|
||||
|
||||
namespace SPMS.Application.DTOs.Account;
|
||||
|
||||
public class ActivityListResponseDto
|
||||
{
|
||||
[JsonPropertyName("items")]
|
||||
public List<ActivityItemDto> Items { get; set; } = new();
|
||||
|
||||
[JsonPropertyName("pagination")]
|
||||
public PaginationDto Pagination { get; set; } = new();
|
||||
}
|
||||
|
||||
public class ActivityItemDto
|
||||
{
|
||||
[JsonPropertyName("activity_type")]
|
||||
public string ActivityType { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("title")]
|
||||
public string Title { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("description")]
|
||||
public string? Description { get; set; }
|
||||
|
||||
[JsonPropertyName("ip_address")]
|
||||
public string? IpAddress { get; set; }
|
||||
|
||||
[JsonPropertyName("occurred_at")]
|
||||
public DateTime OccurredAt { get; set; }
|
||||
}
|
||||
|
|
@ -21,4 +21,10 @@ public class ProfileResponseDto
|
|||
|
||||
[JsonPropertyName("created_at")]
|
||||
public DateTime CreatedAt { get; set; }
|
||||
|
||||
[JsonPropertyName("last_login_at")]
|
||||
public DateTime? LastLoginAt { get; set; }
|
||||
|
||||
[JsonPropertyName("organization")]
|
||||
public string? Organization { get; set; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace SPMS.Application.DTOs.Account;
|
||||
|
||||
|
|
@ -10,4 +11,8 @@ public class UpdateProfileRequestDto
|
|||
[Phone(ErrorMessage = "올바른 전화번호 형식이 아닙니다.")]
|
||||
[StringLength(20, ErrorMessage = "전화번호는 20자 이내여야 합니다.")]
|
||||
public string? Phone { get; set; }
|
||||
|
||||
[JsonPropertyName("organization")]
|
||||
[StringLength(100, ErrorMessage = "소속은 100자 이내여야 합니다.")]
|
||||
public string? Organization { get; set; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,4 +18,5 @@ public interface IAuthService
|
|||
Task IssueTempPasswordAsync(TempPasswordRequestDto request);
|
||||
Task<ProfileResponseDto> GetProfileAsync(long adminId);
|
||||
Task<ProfileResponseDto> UpdateProfileAsync(long adminId, UpdateProfileRequestDto request);
|
||||
Task<ActivityListResponseDto> GetActivityListAsync(long adminId, ActivityListRequestDto request);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ public class AuthService : IAuthService
|
|||
private readonly JwtSettings _jwtSettings;
|
||||
private readonly ITokenStore _tokenStore;
|
||||
private readonly IEmailService _emailService;
|
||||
private readonly IRepository<SystemLog> _systemLogRepository;
|
||||
private readonly ILogger<AuthService> _logger;
|
||||
|
||||
public AuthService(
|
||||
|
|
@ -30,6 +31,7 @@ public class AuthService : IAuthService
|
|||
IOptions<JwtSettings> jwtSettings,
|
||||
ITokenStore tokenStore,
|
||||
IEmailService emailService,
|
||||
IRepository<SystemLog> systemLogRepository,
|
||||
ILogger<AuthService> logger)
|
||||
{
|
||||
_adminRepository = adminRepository;
|
||||
|
|
@ -38,6 +40,7 @@ public class AuthService : IAuthService
|
|||
_jwtSettings = jwtSettings.Value;
|
||||
_tokenStore = tokenStore;
|
||||
_emailService = emailService;
|
||||
_systemLogRepository = systemLogRepository;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
|
@ -610,7 +613,9 @@ public class AuthService : IAuthService
|
|||
Name = admin.Name,
|
||||
Phone = admin.Phone,
|
||||
Role = (int)admin.Role,
|
||||
CreatedAt = admin.CreatedAt
|
||||
CreatedAt = admin.CreatedAt,
|
||||
LastLoginAt = admin.LastLoginAt,
|
||||
Organization = admin.Organization
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -639,6 +644,12 @@ public class AuthService : IAuthService
|
|||
hasChange = true;
|
||||
}
|
||||
|
||||
if (request.Organization is not null && request.Organization != admin.Organization)
|
||||
{
|
||||
admin.Organization = request.Organization;
|
||||
hasChange = true;
|
||||
}
|
||||
|
||||
if (!hasChange)
|
||||
{
|
||||
throw new SpmsException(
|
||||
|
|
@ -657,7 +668,48 @@ public class AuthService : IAuthService
|
|||
Name = admin.Name,
|
||||
Phone = admin.Phone,
|
||||
Role = (int)admin.Role,
|
||||
CreatedAt = admin.CreatedAt
|
||||
CreatedAt = admin.CreatedAt,
|
||||
LastLoginAt = admin.LastLoginAt,
|
||||
Organization = admin.Organization
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<ActivityListResponseDto> GetActivityListAsync(long adminId, ActivityListRequestDto request)
|
||||
{
|
||||
var page = request.Page > 0 ? request.Page : 1;
|
||||
var size = request.Size > 0 ? request.Size : 10;
|
||||
|
||||
// 기간 필터 + AdminId 조건 조합
|
||||
System.Linq.Expressions.Expression<Func<SystemLog, bool>> predicate = log =>
|
||||
log.AdminId == adminId
|
||||
&& (request.From == null || log.CreatedAt >= request.From.Value)
|
||||
&& (request.To == null || log.CreatedAt <= request.To.Value);
|
||||
|
||||
var (items, totalCount) = await _systemLogRepository.GetPagedAsync(
|
||||
page, size,
|
||||
predicate,
|
||||
log => log.CreatedAt,
|
||||
descending: true);
|
||||
|
||||
var totalPages = (int)Math.Ceiling((double)totalCount / size);
|
||||
|
||||
return new ActivityListResponseDto
|
||||
{
|
||||
Items = items.Select(log => new ActivityItemDto
|
||||
{
|
||||
ActivityType = log.Action,
|
||||
Title = log.TargetType ?? log.Action,
|
||||
Description = log.Details,
|
||||
IpAddress = log.IpAddress,
|
||||
OccurredAt = log.CreatedAt
|
||||
}).ToList(),
|
||||
Pagination = new DTOs.Notice.PaginationDto
|
||||
{
|
||||
Page = page,
|
||||
Size = size,
|
||||
TotalCount = totalCount,
|
||||
TotalPages = totalPages
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,4 +23,5 @@ public class Admin : BaseEntity
|
|||
public DateTime AgreedAt { get; set; }
|
||||
public bool MustChangePassword { get; set; }
|
||||
public DateTime? TempPasswordIssuedAt { get; set; }
|
||||
public string? Organization { get; set; }
|
||||
}
|
||||
|
|
|
|||
1253
SPMS.Infrastructure/Migrations/20260226005844_AddOrganizationToAdmin.Designer.cs
generated
Normal file
1253
SPMS.Infrastructure/Migrations/20260226005844_AddOrganizationToAdmin.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,30 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace SPMS.Infrastructure.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddOrganizationToAdmin : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Organization",
|
||||
table: "Admin",
|
||||
type: "varchar(100)",
|
||||
maxLength: 100,
|
||||
nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Organization",
|
||||
table: "Admin");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -79,6 +79,10 @@ namespace SPMS.Infrastructure.Migrations
|
|||
.HasMaxLength(50)
|
||||
.HasColumnType("varchar(50)");
|
||||
|
||||
b.Property<string>("Organization")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)");
|
||||
|
||||
b.Property<string>("Password")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ public class AdminConfiguration : IEntityTypeConfiguration<Admin>
|
|||
builder.Property(e => e.AgreeTerms).HasColumnType("tinyint(1)").IsRequired();
|
||||
builder.Property(e => e.AgreePrivacy).HasColumnType("tinyint(1)").IsRequired();
|
||||
builder.Property(e => e.AgreedAt).IsRequired();
|
||||
builder.Property(e => e.Organization).HasMaxLength(100);
|
||||
|
||||
builder.HasQueryFilter(e => !e.IsDeleted);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user