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 RegisterAsync([FromBody] DeviceRegisterRequestDto request) { var serviceId = GetServiceId(); var result = await _deviceService.RegisterAsync(serviceId, request); return Ok(ApiResponse.Success(result)); } [HttpPost("info")] [SwaggerOperation(Summary = "디바이스 조회", Description = "디바이스 정보를 조회합니다.")] public async Task GetInfoAsync([FromBody] DeviceInfoRequestDto request) { var serviceId = GetServiceId(); var result = await _deviceService.GetInfoAsync(serviceId, request); return Ok(ApiResponse.Success(result)); } [HttpPost("update")] [SwaggerOperation(Summary = "디바이스 수정", Description = "앱 실행 시 디바이스 정보를 업데이트합니다.")] public async Task UpdateAsync([FromBody] DeviceUpdateRequestDto request) { var serviceId = GetServiceId(); await _deviceService.UpdateAsync(serviceId, request); return Ok(ApiResponse.Success()); } [HttpPost("delete")] [SwaggerOperation(Summary = "디바이스 삭제", Description = "앱 삭제/로그아웃 시 디바이스를 비활성화합니다.")] public async Task DeleteAsync([FromBody] DeviceDeleteRequestDto request) { var serviceId = GetServiceId(); await _deviceService.DeleteAsync(serviceId, request); return Ok(ApiResponse.Success()); } [HttpPost("tags")] [SwaggerOperation(Summary = "태그 설정", Description = "디바이스 태그를 설정합니다. 빈 배열 전달 시 모든 태그 해제.")] public async Task SetTagsAsync([FromBody] DeviceTagsRequestDto request) { var serviceId = GetServiceId(); await _deviceService.SetTagsAsync(serviceId, request); return Ok(ApiResponse.Success()); } [HttpPost("agree")] [SwaggerOperation(Summary = "동의 설정", Description = "푸시/마케팅 수신 동의를 설정합니다.")] public async Task 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 AdminDeleteAsync([FromBody] DeviceDeleteRequestDto request) { await _deviceService.AdminDeleteAsync(request.DeviceId); return Ok(ApiResponse.Success()); } [HttpPost("list")] [Authorize] [SwaggerOperation(Summary = "디바이스 목록", Description = "대시보드에서 디바이스 목록을 조회합니다. JWT 인증 필요.")] public async Task GetListAsync([FromBody] DeviceListRequestDto request) { var serviceId = GetOptionalServiceId(); var result = await _deviceService.GetListAsync(serviceId, request); return Ok(ApiResponse.Success(result)); } [HttpPost("export")] [Authorize] [SwaggerOperation(Summary = "기기 엑셀 내보내기", Description = "목록 필터와 동일 조건으로 기기 목록을 엑셀(.xlsx)로 내보냅니다. JWT 인증 필요.")] public async Task 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; } }