improvement: PushWorker 웹훅 발송 연동 (#158) #159
|
|
@ -26,6 +26,7 @@ public class PushWorker : BackgroundService
|
||||||
private readonly IDuplicateChecker _duplicateChecker;
|
private readonly IDuplicateChecker _duplicateChecker;
|
||||||
private readonly IBulkJobStore _bulkJobStore;
|
private readonly IBulkJobStore _bulkJobStore;
|
||||||
private readonly ITokenCacheService _tokenCache;
|
private readonly ITokenCacheService _tokenCache;
|
||||||
|
private readonly IWebhookService _webhookService;
|
||||||
private readonly IFcmSender _fcmSender;
|
private readonly IFcmSender _fcmSender;
|
||||||
private readonly IApnsSender _apnsSender;
|
private readonly IApnsSender _apnsSender;
|
||||||
private readonly ILogger<PushWorker> _logger;
|
private readonly ILogger<PushWorker> _logger;
|
||||||
|
|
@ -37,6 +38,7 @@ public class PushWorker : BackgroundService
|
||||||
IDuplicateChecker duplicateChecker,
|
IDuplicateChecker duplicateChecker,
|
||||||
IBulkJobStore bulkJobStore,
|
IBulkJobStore bulkJobStore,
|
||||||
ITokenCacheService tokenCache,
|
ITokenCacheService tokenCache,
|
||||||
|
IWebhookService webhookService,
|
||||||
IFcmSender fcmSender,
|
IFcmSender fcmSender,
|
||||||
IApnsSender apnsSender,
|
IApnsSender apnsSender,
|
||||||
ILogger<PushWorker> logger)
|
ILogger<PushWorker> logger)
|
||||||
|
|
@ -47,6 +49,7 @@ public class PushWorker : BackgroundService
|
||||||
_duplicateChecker = duplicateChecker;
|
_duplicateChecker = duplicateChecker;
|
||||||
_bulkJobStore = bulkJobStore;
|
_bulkJobStore = bulkJobStore;
|
||||||
_tokenCache = tokenCache;
|
_tokenCache = tokenCache;
|
||||||
|
_webhookService = webhookService;
|
||||||
_fcmSender = fcmSender;
|
_fcmSender = fcmSender;
|
||||||
_apnsSender = apnsSender;
|
_apnsSender = apnsSender;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
|
@ -262,6 +265,21 @@ public class PushWorker : BackgroundService
|
||||||
"푸시 발송 완료: requestId={RequestId}, 성공={Success}, 실패={Fail}, 총={Total}",
|
"푸시 발송 완료: requestId={RequestId}, 성공={Success}, 실패={Fail}, 총={Total}",
|
||||||
pushMessage.RequestId, successCount, failCount, allResults.Count);
|
pushMessage.RequestId, successCount, failCount, allResults.Count);
|
||||||
|
|
||||||
|
// [6.5] 웹훅 발송
|
||||||
|
var webhookEvent = failCount > 0 && successCount == 0
|
||||||
|
? WebhookEvent.PushFailed
|
||||||
|
: WebhookEvent.PushSent;
|
||||||
|
|
||||||
|
_ = _webhookService.SendAsync(pushMessage.ServiceId, webhookEvent, new
|
||||||
|
{
|
||||||
|
request_id = pushMessage.RequestId,
|
||||||
|
message_id = pushMessage.MessageId,
|
||||||
|
send_type = pushMessage.SendType,
|
||||||
|
success_count = successCount,
|
||||||
|
fail_count = failCount,
|
||||||
|
total_count = allResults.Count
|
||||||
|
});
|
||||||
|
|
||||||
// [7] Bulk job 진행률 업데이트
|
// [7] Bulk job 진행률 업데이트
|
||||||
if (!string.IsNullOrEmpty(pushMessage.JobId))
|
if (!string.IsNullOrEmpty(pushMessage.JobId))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
67
TASKS.md
67
TASKS.md
|
|
@ -21,11 +21,11 @@
|
||||||
| **Account** | 9 | 9 | 0 | Phase 2-1 + 3-2 ✅ |
|
| **Account** | 9 | 9 | 0 | Phase 2-1 + 3-2 ✅ |
|
||||||
| **Service** | 13 | 13 | 0 | Phase 2-1 ✅ |
|
| **Service** | 13 | 13 | 0 | Phase 2-1 ✅ |
|
||||||
| **Device** | 7 | 7 | 0 | Phase 2-2 ✅ |
|
| **Device** | 7 | 7 | 0 | Phase 2-2 ✅ |
|
||||||
| **Message** | 5 | 1 | 4 | Phase 3 ← 다음 |
|
| **Message** | 5 | 5 | 0 | Phase 3 ✅ |
|
||||||
| **Push** | 8 | 5 | 3 | Phase 3 |
|
| **Push** | 8 | 8 | 0 | Phase 3 ✅ |
|
||||||
| **Stats** | 5 | 0 | 5 | Phase 3-2 |
|
| **Stats** | 5 | 5 | 0 | Phase 3-2 ✅ |
|
||||||
| **File** | 6 | 6 | 0 | Phase 2-2 ✅ |
|
| **File** | 6 | 6 | 0 | Phase 2-2 ✅ |
|
||||||
| **총계** | **65** | **57** | **8** | - |
|
| **총계** | **65** | **65** | **0** | ✅ 전체 구현 완료 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -1656,9 +1656,9 @@ Milestone: Phase 3: 메시지 & Push Core
|
||||||
| 10 | [Feature] 웹훅 설정 API | Feature | High | WHK-01 | ✅ |
|
| 10 | [Feature] 웹훅 설정 API | Feature | High | WHK-01 | ✅ |
|
||||||
| 11 | [Feature] **웹훅 발송 서비스** | Feature | High | WHK-02 | ✅ |
|
| 11 | [Feature] **웹훅 발송 서비스** | Feature | High | WHK-02 | ✅ |
|
||||||
| 12 | [Feature] **DailyStatWorker 구현** | Feature | Medium | AAG-01 | ✅ |
|
| 12 | [Feature] **DailyStatWorker 구현** | Feature | Medium | AAG-01 | ✅ |
|
||||||
| 13 | [Feature] **DeadTokenCleanupWorker 구현** | Feature | Medium | DTK-01 | ⬜ |
|
| 13 | [Feature] **DeadTokenCleanupWorker 구현** | Feature | Medium | DTK-01 | ✅ |
|
||||||
| 14 | [Feature] **데이터 보관 주기 관리 배치** | Feature | Medium | RET-01 | ⬜ |
|
| 14 | [Feature] **데이터 보관 주기 관리 배치** | Feature | Medium | RET-01 | ✅ |
|
||||||
| 15 | [Feature] **Redis 토큰 캐시 관리** | Feature | Medium | - | ⬜ |
|
| 15 | [Feature] **Redis 토큰 캐시 관리** | Feature | Medium | - | ✅ |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -1750,17 +1750,18 @@ Branch: feature/#XX-daily-stat-worker
|
||||||
제목: [Feature] DeadTokenCleanupWorker 구현 (비활성 토큰 정리)
|
제목: [Feature] DeadTokenCleanupWorker 구현 (비활성 토큰 정리)
|
||||||
Labels: Type/Feature, Priority/Medium, Status/Available
|
Labels: Type/Feature, Priority/Medium, Status/Available
|
||||||
Milestone: Phase 3-2: 통계 & Webhook & 배치
|
Milestone: Phase 3-2: 통계 & Webhook & 배치
|
||||||
Branch: feature/#XX-dead-token-cleanup
|
Branch: feature/#150-dead-token-cleanup
|
||||||
|
Gitea Issue: #150
|
||||||
```
|
```
|
||||||
|
|
||||||
**상태**: ⬜ 대기
|
**상태**: ✅ 완료 (PR #151 머지됨)
|
||||||
|
|
||||||
**설명**: 매주 일요일 03:00 KST에 비활성 상태로 7일 이상 경과한 Device 토큰을 물리 삭제하는 BackgroundService Worker를 구현한다.
|
**설명**: 매주 일요일 03:00 KST에 비활성 상태로 7일 이상 경과한 Device 토큰을 물리 삭제하는 BackgroundService Worker를 구현한다.
|
||||||
|
|
||||||
> **📌 참조**: `Documents/BatchScheduler_Design.md` §5 (DeadTokenCleanupWorker)
|
> **📌 참조**: `Documents/BatchScheduler_Design.md` §5 (DeadTokenCleanupWorker)
|
||||||
|
|
||||||
**체크리스트 — Worker 구현**:
|
**체크리스트 — Worker 구현**:
|
||||||
- [ ] `SPMS.Infrastructure/Workers/DeadTokenCleanupWorker.cs` — BackgroundService 상속
|
- [x] `SPMS.Infrastructure/Workers/DeadTokenCleanupWorker.cs` — BackgroundService 상속
|
||||||
- Cron 스케줄: 매주 일요일 03:00 KST
|
- Cron 스케줄: 매주 일요일 03:00 KST
|
||||||
- 삭제 대상 조회:
|
- 삭제 대상 조회:
|
||||||
```sql
|
```sql
|
||||||
|
|
@ -1777,17 +1778,17 @@ Branch: feature/#XX-dead-token-cleanup
|
||||||
LIMIT 1000
|
LIMIT 1000
|
||||||
```
|
```
|
||||||
- 삭제된 행 > 0 → 반복, = 0 → 완료
|
- 삭제된 행 > 0 → 반복, = 0 → 완료
|
||||||
- Redis 토큰 캐시 무효화 (삭제된 Device 기반)
|
- Redis 토큰 캐시 무효화 → #15 구현 후 연동 예정
|
||||||
- SystemLog에 정리 완료 로그
|
- SystemLog에 정리 완료 로그
|
||||||
|
|
||||||
**체크리스트 — 안전장치**:
|
**체크리스트 — 안전장치**:
|
||||||
- [ ] 배치 단위 삭제 (1000건씩) → DB 부하 분산
|
- [x] 배치 단위 삭제 (1000건씩) → DB 부하 분산
|
||||||
- [ ] 삭제 전 카운트 로깅
|
- [x] 삭제 전 카운트 로깅
|
||||||
- [ ] 비정상 수치 감지 시 중단 (전체의 50% 이상이면 경고)
|
- [x] 비정상 수치 감지 시 중단 (전체의 50% 이상이면 경고)
|
||||||
|
|
||||||
**체크리스트 — 등록**:
|
**체크리스트 — 등록**:
|
||||||
- [ ] `Program.cs` — `builder.Services.AddHostedService<DeadTokenCleanupWorker>()`
|
- [x] `SPMS.Infrastructure/DependencyInjection.cs` — `AddHostedService<DeadTokenCleanupWorker>()`
|
||||||
- [ ] 환경별 활성화 설정 (Debug/Staging: 비활성, Release: 활성)
|
- [ ] 환경별 활성화 설정 (Debug/Staging: 비활성, Release: 활성) → MVP Phase에서 설정
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -1797,22 +1798,23 @@ Branch: feature/#XX-dead-token-cleanup
|
||||||
제목: [Feature] 데이터 보관 주기 관리 배치
|
제목: [Feature] 데이터 보관 주기 관리 배치
|
||||||
Labels: Type/Feature, Priority/Medium, Status/Available
|
Labels: Type/Feature, Priority/Medium, Status/Available
|
||||||
Milestone: Phase 3-2: 통계 & Webhook & 배치
|
Milestone: Phase 3-2: 통계 & Webhook & 배치
|
||||||
Branch: feature/#XX-data-retention
|
Branch: feature/#152-data-retention
|
||||||
|
Gitea Issue: #152
|
||||||
```
|
```
|
||||||
|
|
||||||
**상태**: ⬜ 대기
|
**상태**: ✅ 완료 (PR #153 머지됨)
|
||||||
|
|
||||||
**설명**: 보관 주기가 지난 로그 데이터를 정리하는 배치 작업을 구현한다.
|
**설명**: 보관 주기가 지난 로그 데이터를 정리하는 배치 작업을 구현한다.
|
||||||
|
|
||||||
**체크리스트**:
|
**체크리스트**:
|
||||||
- [ ] `SPMS.Infrastructure/Workers/DataRetentionWorker.cs` — BackgroundService 상속
|
- [x] `SPMS.Infrastructure/Workers/DataRetentionWorker.cs` — BackgroundService 상속
|
||||||
- Cron 스케줄: 매일 04:00 KST
|
- Cron 스케줄: 매일 04:00 KST
|
||||||
- PushSendLog: 90일 이전 데이터 삭제
|
- PushSendLog: 90일 이전 데이터 삭제
|
||||||
- PushOpenLog: 90일 이전 데이터 삭제
|
- PushOpenLog: 90일 이전 데이터 삭제
|
||||||
- WebhookLog: 30일 이전 데이터 삭제
|
- WebhookLog: 30일 이전 데이터 삭제
|
||||||
- SystemLog: 180일 이전 데이터 삭제
|
- SystemLog: 180일 이전 데이터 삭제
|
||||||
- [ ] 배치 단위 삭제 (10000건씩)
|
- [x] 배치 단위 삭제 (10000건씩)
|
||||||
- [ ] SystemLog에 정리 완료 로그
|
- [x] SystemLog에 정리 완료 로그
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -1822,28 +1824,25 @@ Branch: feature/#XX-data-retention
|
||||||
제목: [Feature] Redis 토큰 캐시 관리
|
제목: [Feature] Redis 토큰 캐시 관리
|
||||||
Labels: Type/Feature, Priority/Medium, Status/Available
|
Labels: Type/Feature, Priority/Medium, Status/Available
|
||||||
Milestone: Phase 3-2: 통계 & Webhook & 배치
|
Milestone: Phase 3-2: 통계 & Webhook & 배치
|
||||||
Branch: feature/#XX-redis-token-cache
|
Branch: feature/#154-redis-token-cache
|
||||||
|
Gitea Issue: #154
|
||||||
```
|
```
|
||||||
|
|
||||||
**상태**: ⬜ 대기
|
**상태**: ✅ 완료 (PR #155 머지됨)
|
||||||
|
|
||||||
**설명**: 디바이스 토큰을 Redis에 캐싱하여 DB 조회를 최소화한다.
|
**설명**: 디바이스 토큰을 Redis에 캐싱하여 DB 조회를 최소화한다.
|
||||||
|
|
||||||
> **📌 참조**: MEMORY.md Redis 설정
|
|
||||||
|
|
||||||
**체크리스트 — Infrastructure Layer**:
|
**체크리스트 — Infrastructure Layer**:
|
||||||
- [ ] `SPMS.Infrastructure/Caching/TokenCacheService.cs` — 토큰 캐시 관리
|
- [x] `SPMS.Application/Interfaces/ITokenCacheService.cs` — 인터페이스 + CachedDeviceInfo record
|
||||||
- Key 형식: `device:token:{serviceId}:{userId}`
|
- [x] `SPMS.Infrastructure/Caching/TokenCacheService.cs` — Redis 기반 구현
|
||||||
|
- Key 형식: `device:token:{serviceId}:{deviceId}`
|
||||||
- TTL: 1시간
|
- TTL: 1시간
|
||||||
- GetDeviceTokenAsync(serviceId, userId) → string?
|
- GetDeviceInfoAsync / SetDeviceInfoAsync / InvalidateAsync / InvalidateByServiceAsync
|
||||||
- SetDeviceTokenAsync(serviceId, userId, token)
|
|
||||||
- InvalidateAsync(serviceId, userId)
|
|
||||||
- InvalidateByServiceAsync(serviceId) — 서비스 전체 무효화
|
|
||||||
|
|
||||||
**체크리스트 — PushWorker 연동**:
|
**체크리스트 — PushWorker 연동**:
|
||||||
- [ ] 발송 시 Redis 캐시 우선 조회
|
- [x] 발송 시 Redis 캐시 우선 조회 (single 발송)
|
||||||
- [ ] 캐시 미스 시 DB 조회 후 캐시 저장
|
- [x] 캐시 미스 시 DB 조회 후 캐시 저장
|
||||||
- [ ] Device 삭제/비활성 시 캐시 무효화
|
- [x] Device 등록/수정/삭제/수신동의 변경 시 캐시 무효화
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user