SPMS_API/SPMS.API/Controllers/DeviceController.cs
SEAN a2d563aa9d improvement: 기기 엑셀 내보내기 API 추가 (#241)
- DeviceExportRequestDto: 목록 필터와 동일한 필터 파라미터 (page/size 제외)
- IDeviceRepository/DeviceRepository: GetAllFilteredAsync 추가 (전체 반환)
- DeviceService: ClosedXML 기반 엑셀 생성 (14개 컬럼)
- DeviceController: POST /v1/in/device/export [Authorize] 엔드포인트 추가

Closes #241
2026-02-25 17:16:13 +09:00

121 lines
5.0 KiB
C#

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;
using SPMS.Application.DTOs.Device;
using SPMS.Application.Interfaces;
using SPMS.Domain.Common;
namespace SPMS.API.Controllers;
[ApiController]
[Route("v1/in/device")]
[ApiExplorerSettings(GroupName = "device")]
public class DeviceController : ControllerBase
{
private readonly IDeviceService _deviceService;
public DeviceController(IDeviceService deviceService)
{
_deviceService = deviceService;
}
[HttpPost("register")]
[SwaggerOperation(Summary = "디바이스 등록", Description = "앱 최초 설치 시 디바이스를 등록합니다.")]
public async Task<IActionResult> RegisterAsync([FromBody] DeviceRegisterRequestDto request)
{
var serviceId = GetServiceId();
var result = await _deviceService.RegisterAsync(serviceId, request);
return Ok(ApiResponse<DeviceRegisterResponseDto>.Success(result));
}
[HttpPost("info")]
[SwaggerOperation(Summary = "디바이스 조회", Description = "디바이스 정보를 조회합니다.")]
public async Task<IActionResult> GetInfoAsync([FromBody] DeviceInfoRequestDto request)
{
var serviceId = GetServiceId();
var result = await _deviceService.GetInfoAsync(serviceId, request);
return Ok(ApiResponse<DeviceInfoResponseDto>.Success(result));
}
[HttpPost("update")]
[SwaggerOperation(Summary = "디바이스 수정", Description = "앱 실행 시 디바이스 정보를 업데이트합니다.")]
public async Task<IActionResult> UpdateAsync([FromBody] DeviceUpdateRequestDto request)
{
var serviceId = GetServiceId();
await _deviceService.UpdateAsync(serviceId, request);
return Ok(ApiResponse.Success());
}
[HttpPost("delete")]
[SwaggerOperation(Summary = "디바이스 삭제", Description = "앱 삭제/로그아웃 시 디바이스를 비활성화합니다.")]
public async Task<IActionResult> DeleteAsync([FromBody] DeviceDeleteRequestDto request)
{
var serviceId = GetServiceId();
await _deviceService.DeleteAsync(serviceId, request);
return Ok(ApiResponse.Success());
}
[HttpPost("tags")]
[SwaggerOperation(Summary = "태그 설정", Description = "디바이스 태그를 설정합니다. 빈 배열 전달 시 모든 태그 해제.")]
public async Task<IActionResult> SetTagsAsync([FromBody] DeviceTagsRequestDto request)
{
var serviceId = GetServiceId();
await _deviceService.SetTagsAsync(serviceId, request);
return Ok(ApiResponse.Success());
}
[HttpPost("agree")]
[SwaggerOperation(Summary = "동의 설정", Description = "푸시/마케팅 수신 동의를 설정합니다.")]
public async Task<IActionResult> SetAgreeAsync([FromBody] DeviceAgreeRequestDto request)
{
var serviceId = GetServiceId();
await _deviceService.SetAgreeAsync(serviceId, request);
return Ok(ApiResponse.Success());
}
[HttpPost("admin/delete")]
[Authorize]
[SwaggerOperation(Summary = "관리자 기기 삭제", Description = "관리자가 기기를 삭제(비활성화)합니다. 삭제 즉시 발송이 차단됩니다. JWT 인증 필요.")]
public async Task<IActionResult> AdminDeleteAsync([FromBody] DeviceDeleteRequestDto request)
{
await _deviceService.AdminDeleteAsync(request.DeviceId);
return Ok(ApiResponse.Success());
}
[HttpPost("list")]
[Authorize]
[SwaggerOperation(Summary = "디바이스 목록", Description = "대시보드에서 디바이스 목록을 조회합니다. JWT 인증 필요.")]
public async Task<IActionResult> GetListAsync([FromBody] DeviceListRequestDto request)
{
var serviceId = GetOptionalServiceId();
var result = await _deviceService.GetListAsync(serviceId, request);
return Ok(ApiResponse<DeviceListResponseDto>.Success(result));
}
[HttpPost("export")]
[Authorize]
[SwaggerOperation(Summary = "기기 엑셀 내보내기", Description = "목록 필터와 동일 조건으로 기기 목록을 엑셀(.xlsx)로 내보냅니다. JWT 인증 필요.")]
public async Task<IActionResult> ExportAsync([FromBody] DeviceExportRequestDto request)
{
var serviceId = GetOptionalServiceId();
var fileBytes = await _deviceService.ExportAsync(serviceId, request);
var fileName = $"device_export_{DateTime.UtcNow:yyyyMMdd}.xlsx";
return File(fileBytes, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileName);
}
private long GetServiceId()
{
if (HttpContext.Items.TryGetValue("ServiceId", out var serviceIdObj) && serviceIdObj is long serviceId)
return serviceId;
throw new Domain.Exceptions.SpmsException(ErrorCodes.BadRequest, "서비스 식별 정보가 없습니다.", 400);
}
private long? GetOptionalServiceId()
{
if (HttpContext.Items.TryGetValue("ServiceId", out var obj) && obj is long id)
return id;
return null;
}
}