Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified .DS_Store
Binary file not shown.
61 changes: 61 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
name: CI

on:
pull_request:
branches:
- dev
- main
workflow_dispatch:

concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build-and-test:
runs-on: ubuntu-latest
permissions:
contents: read
checks: write
pull-requests: write

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'corretto'
cache: gradle

- name: Grant execute permission for gradlew
run: chmod +x gradlew

- name: Compile Check
run: ./gradlew compileJava compileTestJava

- name: Run Tests
env:
SPRING_PROFILES_ACTIVE: ci
run: ./gradlew test

- name: Publish Test Results
uses: EnricoMi/publish-unit-test-result-action@v2
if: always()
with:
files: build/test-results/test/*.xml
check_name: "Test Results"

- name: Build JAR (verify build)
run: ./gradlew bootJar

- name: Build Summary
if: always()
run: |
echo "### CI Summary :white_check_mark:" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **Branch:** ${{ github.head_ref }}" >> $GITHUB_STEP_SUMMARY
echo "- **Commit:** ${{ github.sha }}" >> $GITHUB_STEP_SUMMARY
echo "- **Event:** ${{ github.event_name }}" >> $GITHUB_STEP_SUMMARY
9 changes: 9 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-actuator'

// TestContainer
testImplementation "org.testcontainers:testcontainers:1.19.3"
testImplementation "org.testcontainers:junit-jupiter:1.19.3"
testImplementation "org.testcontainers:mysql:1.19.3"

// Micrometer
implementation 'io.micrometer:micrometer-registry-prometheus' // Prometheus

Expand Down Expand Up @@ -65,6 +70,10 @@ dependencies {
testImplementation 'org.testcontainers:jdbc'
testImplementation 'org.testcontainers:junit-jupiter'

// flyway
implementation 'org.flywaydb:flyway-core:11.20.1'
implementation 'org.flywaydb:flyway-mysql'

}

tasks.named('test') {
Expand Down
Binary file modified src/.DS_Store
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
package com.todaysound.todaysound_server.domain.alarm.controller;

import com.todaysound.todaysound_server.domain.alarm.dto.request.SummaryReadRequestDto;
import com.todaysound.todaysound_server.domain.alarm.dto.response.RecentAlarmResponse;
import com.todaysound.todaysound_server.domain.alarm.dto.response.UnreadAlarmResponse;
import com.todaysound.todaysound_server.global.dto.PageRequestDTO;
import com.todaysound.todaysound_server.global.exception.CustomErrorResponse;
import com.todaysound.todaysound_server.global.response.ApiResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;

import java.util.List;
Expand Down Expand Up @@ -49,8 +45,7 @@ public interface AlarmApi {
"subscriptionId": 1,
"alias": "λ™κ΅­λŒ€ SW μœ΅ν•©κ΅μœ‘μ›",
"summaryContent": "μš”μ•½λœ μ•Œλ¦Ό λ‚΄μš©...",
"timeAgo": "5λΆ„ μ „",
"urgent": true
"timeAgo": "5λΆ„ μ „"
}
]
}
Expand Down Expand Up @@ -101,85 +96,6 @@ List<RecentAlarmResponse> getRecentAlarms(
@RequestHeader("X-Device-Secret") String deviceSecret
);

@Operation(
summary = "읽지 μ•Šμ€ μ•ŒλžŒ 쑰회 (메인 ν™”λ©΄μš©)",
description = """
메인 ν™”λ©΄μ—μ„œ μ‚¬μš©ν•  읽지 μ•Šμ€ μ•ŒλžŒ λͺ©λ‘μ„ μ‘°νšŒν•©λ‹ˆλ‹€.
각 κ΅¬λ…λ³„λ‘œ 읽지 μ•Šμ€ Summary만 ν¬ν•¨ν•˜μ—¬ λ°˜ν™˜ν•©λ‹ˆλ‹€.
""",
tags = {"Alarm"},
operationId = "getUnreadAlarmsForMain"
)
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(
responseCode = "200",
description = "읽지 μ•Šμ€ μ•ŒλžŒ 쑰회 성곡",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = ApiResponse.class),
examples = @ExampleObject(
name = "읽지 μ•Šμ€ μ•ŒλžŒ λͺ©λ‘ μ˜ˆμ‹œ",
value = """
{
"errorCode": null,
"message": "OK",
"result": [
{
"subscriptionId": 1,
"alias": "λ™κ΅­λŒ€ SW μœ΅ν•©κ΅μœ‘μ›",
"url": "https://example.com",
"timeAgo": "10λΆ„ μ „",
"urgent": false,
"unreadCount": 3,
"unreadSummaries": [
{
"id": 1,
"content": "μš”μ•½ λ‚΄μš©...",
"updatedAt": "2025-01-01T12:00:00"
}
]
}
]
}
"""
)
)
)
})
List<UnreadAlarmResponse> getUnreadAlarmsForMain(
@ModelAttribute PageRequestDTO pageRequest,
@RequestHeader("X-User-ID") String userUuid,
@RequestHeader("X-Device-Secret") String deviceSecret
);

@Operation(
summary = "μš”μ•½ 읽음 처리",
description = """
μ‚¬μš©μžκ°€ μš”μ•½(Summary)을 μ½μ—ˆμ„ λ•Œ ν•΄λ‹Ή μš”μ•½λ“€μ„ 읽음 μ²˜λ¦¬ν•©λ‹ˆλ‹€.
summaryIds λͺ©λ‘μ„ μ „λ‹¬ν•˜λ©΄ ν•΄λ‹Ή IDλ“€μ˜ μš”μ•½μ΄ 읽음 μ²˜λ¦¬λ©λ‹ˆλ‹€.
""",
tags = {"Alarm"},
operationId = "markSummaryAsRead"
)
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(
responseCode = "200",
description = "μš”μ•½ 읽음 처리 성곡"
),
@io.swagger.v3.oas.annotations.responses.ApiResponse(
responseCode = "400",
description = "잘λͺ»λœ μš”μ²­ 데이터",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = CustomErrorResponse.class)
)
)
})
void markSummaryAsRead(
@RequestBody SummaryReadRequestDto summaryReadRequestDto,
@RequestHeader("X-User-ID") String userUuid,
@RequestHeader("X-Device-Secret") String deviceSecret
);
}


Original file line number Diff line number Diff line change
@@ -1,63 +1,19 @@
package com.todaysound.todaysound_server.domain.alarm.controller;

import java.util.List;

import com.todaysound.todaysound_server.domain.alarm.dto.request.SummaryReadRequestDto;
import org.springframework.web.bind.annotation.*;
import com.todaysound.todaysound_server.domain.alarm.dto.response.RecentAlarmResponse;
import com.todaysound.todaysound_server.domain.alarm.dto.response.UnreadAlarmResponse;
import com.todaysound.todaysound_server.domain.alarm.service.AlarmQueryService;
import com.todaysound.todaysound_server.domain.summary.service.SummaryCommandService;
import com.todaysound.todaysound_server.global.dto.PageRequestDTO;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/alarms")
@RequiredArgsConstructor
public class AlarmController implements AlarmApi {

private final AlarmQueryService alarmQueryService;
private final SummaryCommandService summaryCommandService;

/**
* 졜근 μ•Œλ¦Ό λͺ©λ‘ 쑰회
*/
@GetMapping()
@Override
public List<RecentAlarmResponse> getRecentAlarms(
@ModelAttribute final PageRequestDTO pageRequest,
@RequestHeader("X-User-ID") String userUuid,
@RequestHeader("X-Device-Secret") String deviceSecret) {

return alarmQueryService.getRecentAlarms(pageRequest, userUuid, deviceSecret);
}



/**
* λ©”μΈν™”λ©΄μš© 읽지 μ•Šμ€ μ•ŒλžŒ 쑰회 - 읽지 μ•Šμ€ Summary만 ν¬ν•¨ν•˜μ—¬ λ°˜ν™˜
*/
@GetMapping("/unread")
@Override
public List<UnreadAlarmResponse> getUnreadAlarmsForMain(
@ModelAttribute final PageRequestDTO pageRequest,
@RequestHeader("X-User-ID") String userUuid,
@RequestHeader("X-Device-Secret") String deviceSecret) {

return alarmQueryService.getUnreadAlarmsForMain(pageRequest, userUuid, deviceSecret);
}


/**
* Summary 읽음 처리 - ν”„λ‘ νŠΈμ—μ„œ μ‚¬μš©μžκ°€ Summaryλ₯Ό μ½μ—ˆμ„ λ•Œ 호좜
*/
@PatchMapping("/summaries/read")
@Override
public void markSummaryAsRead(@RequestBody SummaryReadRequestDto summaryReadRequestDto,
@RequestHeader("X-User-ID") String userUuid,
@RequestHeader("X-Device-Secret") String deviceSecret) {

summaryCommandService.markSummaryAsRead(summaryReadRequestDto, userUuid, deviceSecret);
}
public class AlarmController {
//
// @DeleteMapping
// public void deleteAlarms(@RequestMapping) {
//
// }

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.todaysound.todaysound_server.domain.alarm.controller;

import java.util.List;

import org.springframework.web.bind.annotation.*;
import com.todaysound.todaysound_server.domain.alarm.dto.response.RecentAlarmResponse;
import com.todaysound.todaysound_server.domain.alarm.service.AlarmQueryService;
import com.todaysound.todaysound_server.domain.summary.service.SummaryCommandService;
import com.todaysound.todaysound_server.global.dto.PageRequestDTO;
import lombok.RequiredArgsConstructor;

@RestController
@RequestMapping("/api/alarms")
@RequiredArgsConstructor
public class AlarmQueryController implements AlarmApi {

private final AlarmQueryService alarmQueryService;
private final SummaryCommandService summaryCommandService;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

SummaryCommandServiceκ°€ μ£Όμž…λ˜μ—ˆμ§€λ§Œ 이 클래슀 λ‚΄μ—μ„œ μ‚¬μš©λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. μ‚¬μš©ν•˜μ§€ μ•ŠλŠ” μ˜μ‘΄μ„±μ€ μ œκ±°ν•˜μ—¬ μ½”λ“œμ˜ λͺ…확성을 높이고 λΆˆν•„μš”ν•œ μžμ› 할당을 λ°©μ§€ν•˜λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€.

Suggested change
private final SummaryCommandService summaryCommandService;
private final AlarmQueryService alarmQueryService;


/**
* 졜근 μ•Œλ¦Ό λͺ©λ‘ 쑰회
*/
@GetMapping()
@Override
public List<RecentAlarmResponse> getRecentAlarms(
@ModelAttribute final PageRequestDTO pageRequest,
@RequestHeader("X-User-ID") String userUuid,
@RequestHeader("X-Device-Secret") String deviceSecret) {

return alarmQueryService.getRecentAlarms(pageRequest, userUuid, deviceSecret);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public interface InternalAlertApi {
summary = "μ•Œλ¦Ό 생성 (크둀러용)",
description = """
ν¬λ‘€λŸ¬κ°€ μƒˆλ‘œμš΄ κ²Œμ‹œκΈ€μ„ κ°μ§€ν–ˆμ„ λ•Œ μ•Œλ¦Όμ„ μƒμ„±ν•˜κΈ° μœ„ν•œ μ—”λ“œν¬μΈνŠΈμž…λ‹ˆλ‹€.
user_id, subscription_id, site_post_id, title, url, content_raw, content_summary, is_urgent 정보λ₯Ό μ „λ‹¬ν•©λ‹ˆλ‹€.
user_id, subscription_id, site_post_id, title, url, content_raw, content_summary, keyword_matched 정보λ₯Ό μ „λ‹¬ν•©λ‹ˆλ‹€.
""",
tags = {"InternalAlert"},
operationId = "createInternalAlert"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@
* "user_id": 10,
* "subscription_id": 1,
* "site_post_id": "12345",
* "title": "κ²Œμ‹œκΈ€ 제λͺ©",
* "title": "κ²Œμ‹œκΈ€ 제λͺ©",
* "url": "https://...",
* "content_raw": "...원문...",
* "content_summary": "...μš”μ•½...",
* "is_urgent": true
* "keyword_matched": true
* }
*/
@RestController
Expand All @@ -49,13 +49,23 @@ public void createAlert(@RequestBody InternalAlertRequest request) {
throw BaseException.type(CommonErrorCode.FORBIDDEN);
}

User user = subscription.getUser();
// μ•Œλ¦Όμ΄ ν™œμ„±ν™”λœ ꡬ독에 λŒ€ν•΄μ„œλ§Œ ν‘Έμ‹œ 전솑
if (subscription.isAlarmEnabled()) {
User user = subscription.getUser();
String prefix;

fcmService.sendNotificationToUser(
user,
"μƒˆ μ•Œλ¦Ό: " + request.title(),
request.contentSummary()
);
if(request.keywordMatched == true) {
prefix = "[" + request.siteAlias + "]";
} else {
prefix = "[κΈ΄κΈ‰/" + request.siteAlias + "]";
}

fcmService.sendNotificationToUser(
user,
prefix + request.title(),
request.contentSummary()
);
}

// sitePostId λ₯Ό ν•΄μ‹œ ν‚€λ‘œ μ‚¬μš©
Summary summary = Summary.create(
Expand All @@ -64,6 +74,7 @@ public void createAlert(@RequestBody InternalAlertRequest request) {
request.contentSummary(),
request.url(),
request.publishedAt(),
request.keywordMatched,
subscription
);

Expand All @@ -80,7 +91,6 @@ public record InternalAlertRequest(
@JsonProperty("published_at") String publishedAt,
@JsonProperty("content_raw") String contentRaw,
@JsonProperty("content_summary") String contentSummary,
@JsonProperty("is_urgent") boolean isUrgent,
@JsonProperty("keyword_matched") boolean keywordMatched
) {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ public record RecentAlarmResponse(
String summaryContent,
String postUrl,
String timeAgo,
boolean isUrgent,
boolean isRead
boolean isKeywordMatched
) {

public static RecentAlarmResponse of(Summary summary) {
Expand All @@ -23,8 +22,7 @@ public static RecentAlarmResponse of(Summary summary) {
summary.getContent(),
summary.getPostUrl(),
TimeUtil.toRelativeTime(summary.getUpdatedAt()),
summary.getSubscription().isUrgent(),
summary.isRead()
summary.isKeywordMatched()

);
}
Expand Down
Loading