-
Notifications
You must be signed in to change notification settings - Fork 0
[Feat] #20 트래킹 세션 종료 시 하이킹 기록 생성 #54
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
d3ce2f1
7a65656
43242da
b412b69
2dd76e2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,10 @@ | |
|
|
||
| import com.semosan.api.common.exception.GeneralException; | ||
| import com.semosan.api.common.status.ErrorStatus; | ||
| import com.semosan.api.domain.hiking.entity.HikingMember; | ||
| import com.semosan.api.domain.hiking.entity.HikingRecord; | ||
| import com.semosan.api.domain.hiking.repository.HikingMemberRepository; | ||
| import com.semosan.api.domain.hiking.repository.HikingRecordRepository; | ||
| import com.semosan.api.domain.mountain.entity.Course; | ||
| import com.semosan.api.domain.mountain.entity.Mountain; | ||
| import com.semosan.api.domain.mountain.repository.CourseRepository; | ||
|
|
@@ -33,6 +37,9 @@ public class TrackingSessionService { | |
| private final MountainRepository mountainRepository; | ||
| private final CourseRepository courseRepository; | ||
| private final UserReader userReader; | ||
| private final TrackingSessionStatsService statsService; | ||
| private final HikingRecordRepository hikingRecordRepository; | ||
| private final HikingMemberRepository hikingMemberRepository; | ||
|
|
||
| @Transactional | ||
| public TrackingSessionResponse create(Long userId, CreateTrackingSessionRequest request) { | ||
|
|
@@ -75,14 +82,31 @@ public TrackingSessionResponse resume(Long userId, Long sessionId) { | |
| } | ||
|
|
||
| /** | ||
| * 정상 종료. 본 메서드는 상태/시각만 마감하고, 실제 HikingRecord 변환은 #20 에서 추가될 예정. | ||
| * TODO(#20): 종료 시 Redis Stream 의 GPS 점들을 모아 통계 계산 + HikingRecord/HikingMember 생성. | ||
| * 정상 종료. 세션 상태를 COMPLETED 로 마감하고, Redis Hash 의 실시간 통계를 스냅샷 떠 | ||
| * HikingRecord + HikingMember 로 영구 변환한다. | ||
| * - 통계는 GPS Consumer 가 메시지 수신 즉시 갱신해두므로 종료 시점의 스냅샷이 최신값. | ||
| * - 동행자 기능 미구현이므로 본인 1명만 HikingMember 로 등록. | ||
| * - cliveImageUrl / photoReportImageUrl 은 #46 사진 흐름에서 채워질 예정 (현재 null). | ||
| */ | ||
| @Transactional | ||
| public TrackingSessionResponse complete(Long userId, Long sessionId) { | ||
| TrackingSession session = findOwnedSession(userId, sessionId); | ||
| session.complete(); | ||
| return TrackingSessionResponse.from(session); | ||
|
|
||
| TrackingSessionStatsService.Stats stats = statsService.getStats(sessionId); | ||
| HikingRecord record = HikingRecord.fromTrackingSession( | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. stats의 pointCount가 0이면 통계 데이터 없음으로 간주하고 경고 처리나 예외 처리하면 좋을 듯!
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 좋습니다 일단 예외는 안넣고 로그만 넣었습니다 |
||
| session, | ||
| stats.distanceMeters(), | ||
| stats.maxAltitudeMeters(), | ||
| stats.ascentMeters(), | ||
| stats.descentMeters() | ||
| ); | ||
| HikingRecord savedRecord = hikingRecordRepository.save(record); | ||
|
|
||
| HikingMember member = HikingMember.create(savedRecord, session.getUser()); | ||
| hikingMemberRepository.save(member); | ||
|
|
||
| return TrackingSessionResponse.from(session, savedRecord.getId()); | ||
| } | ||
|
|
||
| @Transactional | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,53 @@ | ||||||||||||||||||||||||||||||||||||||||
| -- ===================================================================== | ||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 파일명도 바꿔야 할듯
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 수정 완료 |
||||||||||||||||||||||||||||||||||||||||
| -- 002_hiking_record_extension.sql | ||||||||||||||||||||||||||||||||||||||||
| -- 목적: 트래킹 흐름에서 생성될 HikingRecord 가 가져야 할 필드들 추가. | ||||||||||||||||||||||||||||||||||||||||
| -- - 트래킹 세션 참조 (tracking_session_id) | ||||||||||||||||||||||||||||||||||||||||
| -- - 시간 정보 (started_at, ended_at, paused_seconds_total) | ||||||||||||||||||||||||||||||||||||||||
| -- - GPS 통계 (distance, ascent, descent) | ||||||||||||||||||||||||||||||||||||||||
| -- - 자유 기록 대응 (course_id NOT NULL → NULLABLE) | ||||||||||||||||||||||||||||||||||||||||
| -- 적용 시점: PR #20 머지 직후. 1회성. | ||||||||||||||||||||||||||||||||||||||||
| -- idempotent: ADD COLUMN IF NOT EXISTS / DROP NOT NULL 사용. | ||||||||||||||||||||||||||||||||||||||||
| -- ===================================================================== | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| -- 1) course_id 를 nullable 로 (자유 기록 대응) | ||||||||||||||||||||||||||||||||||||||||
| ALTER TABLE hiking_records | ||||||||||||||||||||||||||||||||||||||||
| ALTER COLUMN course_id DROP NOT NULL; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| -- 2) 트래킹 세션 참조 | ||||||||||||||||||||||||||||||||||||||||
| ALTER TABLE hiking_records | ||||||||||||||||||||||||||||||||||||||||
| ADD COLUMN IF NOT EXISTS tracking_session_id BIGINT; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| -- 3) 시간 정보 | ||||||||||||||||||||||||||||||||||||||||
| ALTER TABLE hiking_records | ||||||||||||||||||||||||||||||||||||||||
| ADD COLUMN IF NOT EXISTS started_at TIMESTAMP; | ||||||||||||||||||||||||||||||||||||||||
| ALTER TABLE hiking_records | ||||||||||||||||||||||||||||||||||||||||
| ADD COLUMN IF NOT EXISTS ended_at TIMESTAMP; | ||||||||||||||||||||||||||||||||||||||||
| ALTER TABLE hiking_records | ||||||||||||||||||||||||||||||||||||||||
| ADD COLUMN IF NOT EXISTS paused_seconds_total INTEGER NOT NULL DEFAULT 0; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| -- 4) GPS 통계 | ||||||||||||||||||||||||||||||||||||||||
| ALTER TABLE hiking_records | ||||||||||||||||||||||||||||||||||||||||
| ADD COLUMN IF NOT EXISTS distance DOUBLE PRECISION; | ||||||||||||||||||||||||||||||||||||||||
| ALTER TABLE hiking_records | ||||||||||||||||||||||||||||||||||||||||
| ADD COLUMN IF NOT EXISTS ascent DOUBLE PRECISION; | ||||||||||||||||||||||||||||||||||||||||
| ALTER TABLE hiking_records | ||||||||||||||||||||||||||||||||||||||||
| ADD COLUMN IF NOT EXISTS descent DOUBLE PRECISION; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| -- 5) FK 제약 (tracking_sessions 테이블이 먼저 존재해야 함 — 본 마이그레이션 이전에 tracking_sessions DDL 적용 필요) | ||||||||||||||||||||||||||||||||||||||||
| DO $$ | ||||||||||||||||||||||||||||||||||||||||
| BEGIN | ||||||||||||||||||||||||||||||||||||||||
| IF NOT EXISTS ( | ||||||||||||||||||||||||||||||||||||||||
| SELECT 1 | ||||||||||||||||||||||||||||||||||||||||
| FROM information_schema.table_constraints | ||||||||||||||||||||||||||||||||||||||||
| WHERE table_name = 'hiking_records' | ||||||||||||||||||||||||||||||||||||||||
| AND constraint_name = 'fk_hiking_records_tracking_session' | ||||||||||||||||||||||||||||||||||||||||
| ) THEN | ||||||||||||||||||||||||||||||||||||||||
| ALTER TABLE hiking_records | ||||||||||||||||||||||||||||||||||||||||
| ADD CONSTRAINT fk_hiking_records_tracking_session | ||||||||||||||||||||||||||||||||||||||||
| FOREIGN KEY (tracking_session_id) REFERENCES tracking_sessions(id); | ||||||||||||||||||||||||||||||||||||||||
| END IF; | ||||||||||||||||||||||||||||||||||||||||
| END$$; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| -- 6) 조회 인덱스 (HikingRecord 로 트래킹 세션 역방향 찾을 일은 거의 없으나, 디버그/분석용) | ||||||||||||||||||||||||||||||||||||||||
| CREATE INDEX IF NOT EXISTS idx_hiking_records_tracking_session | ||||||||||||||||||||||||||||||||||||||||
| ON hiking_records (tracking_session_id); | ||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Enforce one-record-per-session at DB level.
Suggested migration adjustment DO $$
BEGIN
IF NOT EXISTS (
SELECT 1
FROM information_schema.table_constraints
WHERE table_name = 'hiking_records'
AND constraint_name = 'fk_hiking_records_tracking_session'
) THEN
ALTER TABLE hiking_records
ADD CONSTRAINT fk_hiking_records_tracking_session
FOREIGN KEY (tracking_session_id) REFERENCES tracking_sessions(id);
END IF;
END$$;
-CREATE INDEX IF NOT EXISTS idx_hiking_records_tracking_session
- ON hiking_records (tracking_session_id);
+CREATE UNIQUE INDEX IF NOT EXISTS uq_hiking_records_tracking_session
+ ON hiking_records (tracking_session_id)
+ WHERE tracking_session_id IS NOT NULL;📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 여기 unique 제약 필요할 듯!
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 추가완료 |
||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이거 columne name이랑 entity 필드 명이랑 불일치
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
수정완료!