Skip to content

Commit 4af0eaa

Browse files
authored
Merge pull request #120 from codeit-garden/#100
fix: 연속심기 미션 업데이트 from 은수
2 parents aaa45ca + aa879f2 commit 4af0eaa

File tree

3 files changed

+121
-57
lines changed

3 files changed

+121
-57
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141

4242
## 구현 세부 사항
4343

44-
## 1. 집중 시간 생겅 및 관리 로직
44+
## 1. 집중 시간 생성 및 관리 로직
4545

4646
### **이벤트 기반 집중 시간 관리 (Redis 활용)**
4747
- 집중 시간이 생성되면 시간으로 Redis Sorted Set(`zAdd`)을 사용해 실행 예약
@@ -62,8 +62,8 @@
6262
- sse로 전달한 데이터를 띄우고 집중시간 생성, 종료만 클라이언트에서 실행하게 서버에서 관리하려고 함
6363

6464
### **레디스를 사용한 이유**
65-
- 모든 집중시간을 `setInterval`로 관리하면 너무 많은 이벤트가 서버에 등록됌
66-
- 클라이언트의 조회와 이벤트 실행이 모두 데이터 베이스 요청으로 처리 시 부하가 커질 것 같다고 생각됌
65+
- 모든 집중시간을 `setInterval`로 관리하면 너무 많은 이벤트가 서버에 등록됨
66+
- 클라이언트의 조회와 이벤트 실행이 모두 데이터 베이스 요청으로 처리 시 부하가 커질 것 같다고 생각됨
6767
- 이를 위해 레디스를 사용하여 레디스에 시간을 기준으로 이벤트를 등록하여 해당 시간이 되면 이벤트를 실행하게 함
6868

6969
### **SSE를 사용한 이유**

src/services/missionService.js

Lines changed: 91 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -62,15 +62,46 @@ const uncompletedMission = async (memberId) => {
6262

6363
//연속 심기 미션 업데이트(로그인시..?)
6464
const updateConsecutivePlantingMission = async(memberId) => {
65-
const today = new Date(new Date().setHours(0, 0, 0, 0)); //시간 자정으로 맞춤
65+
// UTC 시간을 KST로 변환 (UTC+9)
66+
const now = new Date();
67+
const kstOffset = 9 * 60 * 60 * 1000;
68+
const today = new Date(new Date(now.getTime() + kstOffset).setHours(0, 0, 0, 0));
6669
const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000);
6770

6871
try{
72+
// DB의 UTC 시간을 KST 기준으로 조회
73+
const todayPlantings = await prisma.focusTime.count({
74+
where: {
75+
memberId: Number(memberId),
76+
createdAt: {
77+
// UTC로 변환하여 쿼리
78+
gte: new Date(today.getTime() - kstOffset),
79+
lt: new Date(today.getTime() + 24 * 60 * 60 * 1000 - kstOffset),
80+
},
81+
},
82+
});
83+
84+
// 오늘 심은 꽃이 없으면 미션 업데이트하지 않음
85+
if (todayPlantings === 0) {
86+
return [];
87+
}
88+
89+
//어제 심었는지 확인
90+
const yesterdayPlantings = await prisma.focusTime.count({
91+
where: {
92+
memberId: Number(memberId),
93+
createdAt: {
94+
gte: new Date(yesterday.getTime() - kstOffset),
95+
lt: new Date(yesterday.getTime() + 24 * 60 * 60 * 1000 - kstOffset),
96+
},
97+
},
98+
});
99+
69100
const missions = await prisma.memberMission.findMany({
70101
where: {
71102
memberId,
72103
mission: { type: 'CONSECUTIVE_PLANTING' },
73-
NOT: { lastUpdated: { gte: new Date(today) } }, //오늘 이미 갱신된 미션 제외
104+
NOT: { lastUpdated: { gte: today } }, //오늘 이미 갱신된 미션 제외
74105
completed: false,
75106
},
76107
include: {mission: { include: { flower: true } } },
@@ -79,61 +110,73 @@ const updateConsecutivePlantingMission = async(memberId) => {
79110
const completedMissions = [];
80111

81112
for (const plantingMission of missions){
82-
let reset = false;
83-
113+
let reset = yesterdayPlantings === 0; //어제 focusTime이 없으면 reset
114+
// DB의 UTC 시간을 KST로 변환
115+
const lastUpdated = plantingMission.lastUpdated ?
116+
new Date(plantingMission.lastUpdated.getTime() + kstOffset) : null;
117+
const startDate = plantingMission.startDate ?
118+
new Date(plantingMission.startDate.getTime() + kstOffset) : null;
119+
84120
//날짜 계산 편하게 자정으로 다 맞춤
85-
const lastUpdated = plantingMission.lastUpdated? new Date(new Date(plantingMission.lastUpdated).setHours(0, 0, 0, 0)) : null;
86-
const startDate = plantingMission.startDate? new Date(new Date(plantingMission.startDate).setHours(0, 0, 0, 0)) : null;
87-
88-
//마지막 업데이트가 어제 이전이면 연속심기 초기화
89-
if(!lastUpdated || lastUpdated < yesterday ){
90-
reset = true;
91-
}
92-
if(reset || !startDate){
93-
//미션 초기화 또는 새로 시작
94-
await prisma.memberMission.update({
95-
where: {id :plantingMission.id},
96-
data: {
97-
startDate: today,
98-
completed: false,
99-
lastUpdated: today,
100-
},
101-
});
102-
}else{
103-
//연속심기 미션 완료한 경우
104-
const days = startDate?Math.floor((today - startDate) / (24 * 60 * 60 * 1000)): 0; //연속 일자
105-
if(days >= plantingMission.mission.targetValue -1){
106-
await prisma.memberMission.update({
107-
where: {id: plantingMission.id},
108-
data: {
109-
completed: true,
110-
lastUpdated: today,
111-
},
112-
});
113-
await missionUtils.unlockFlower(memberId, plantingMission.mission.flower.id);
114-
completedMissions.push({
115-
missionId: plantingMission.id,
116-
flower: plantingMission.mission.flower?
117-
{
118-
name: plantingMission.mission.flower.name,
119-
FlowerImg: plantingMission.mission.flower.FlowerImg
120-
} : null //해당 미션으로 깨지는 꽃이 없는 경우 'null'반환
121-
});
122-
}else{
123-
//연속 심기 진행 중이지만 아직 완료되지 않은 경우
121+
//if (lastUpdated) lastUpdated.setHours(0, 0, 0, 0);
122+
//if (startDate) startDate.setHours(0, 0, 0, 0);
123+
124+
//reset = false;
125+
126+
//마지막 업데이트가 어제가 아니면 연속심기 초기화
127+
//if (!lastUpdated || lastUpdated.getTime() !== yesterday.getTime()) {
128+
// reset = true;
129+
//}
130+
131+
if (reset || !startDate) {
132+
//미션 초기화 또는 새로 시작
124133
await prisma.memberMission.update({
125134
where: {id: plantingMission.id},
126135
data: {
127-
lastUpdated: today, //lastUpdated만 업데이트
136+
// KST를 다시 UTC로 변환하여 저장
137+
startDate: new Date(today.getTime() - kstOffset),
138+
completed: false,
139+
lastUpdated: new Date(today.getTime() - kstOffset),
128140
},
129141
});
130-
}
142+
} else {
143+
const days = Math.floor((today - startDate) / (24 * 60 * 60 * 1000)) + 1;
144+
145+
if (days >= plantingMission.mission.targetValue) {
146+
//연속심기 미션 완료한 경우
147+
await prisma.memberMission.update({
148+
where: {id: plantingMission.id},
149+
data: {
150+
completed: true,
151+
// KST를 다시 UTC로 변환하여 저장
152+
lastUpdated: new Date(today.getTime() - kstOffset),
153+
},
154+
});
155+
await missionUtils.unlockFlower(memberId, plantingMission.mission.flower.id);
156+
completedMissions.push({
157+
missionId: plantingMission.id,
158+
flower: plantingMission.mission.flower ? {
159+
name: plantingMission.mission.flower.name,
160+
FlowerImg: plantingMission.mission.flower.FlowerImg
161+
} : null //해당 미션으로 깨지는 꽃이 없는 경우 'null'반환
162+
});
163+
} else {
164+
//연속 심기 진행 중이지만 아직 완료되지 않은 경우
165+
await prisma.memberMission.update({
166+
where: {id: plantingMission.id},
167+
data: {
168+
// KST를 다시 UTC로 변환하여 저장
169+
lastUpdated: new Date(today.getTime() - kstOffset),
170+
},
171+
});
172+
}
131173
}
132174
}
133175
//console.log(completedMissions); //점검용
134-
return completedMissions; //완료한 미션 반환
135-
}catch(error){
136-
throw new CustomError(ErrorCodes.InternalServerError, '연속 미션 업데이트 중 오류가 발생하였습니다.');
176+
return completedMissions;
177+
} catch(error) {
178+
console.error(error);
179+
throw new CustomError(ErrorCodes.InternalServerError, '연속 심기 미션 업데이트 중 오류가 발생하였습니다.');
137180
}
138181
};
139182

src/utils/missionUtils.js

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const prisma = new PrismaClient();
99
const calculateCurrentValue = async (memberId, mission) => {
1010
switch(mission.mission.type){
1111
case 'CONSECUTIVE_PLANTING':
12-
return await calculateConsecutivePlantingMissionValue(mission);
12+
return await calculateConsecutivePlantingMissionValue(memberId, mission);
1313
case 'FOCUS_TIME':
1414
return await calculateFocusTimeMissionValue(memberId);
1515
case 'TOTAL_FLOWERS':
@@ -22,11 +22,32 @@ const calculateCurrentValue = async (memberId, mission) => {
2222
/**
2323
* 연속 심기 미션 currentValue 계산
2424
*/
25-
const calculateConsecutivePlantingMissionValue = (mission) => {
26-
const today = new Date(new Date().setHours(0, 0, 0, 0));
27-
const startDate = mission.startDate ? new Date(new Date(mission.startDate).setHours(0, 0, 0, 0)) : null;
28-
value = startDate?Math.floor((today - startDate) / (24 * 60 * 60 * 1000)): 0;
29-
return value;
25+
const calculateConsecutivePlantingMissionValue = async(memberId, mission) => {
26+
if (!mission.startDate) return 0;
27+
28+
// UTC -> KST 변환
29+
const kstOffset = 9 * 60 * 60 * 1000;
30+
const today = new Date(new Date(Date.now() + kstOffset).setHours(0, 0, 0, 0));
31+
const startDate = new Date(new Date(mission.startDate.getTime() + kstOffset).setHours(0, 0, 0, 0));
32+
33+
//오늘 focusTime 있는지 확인
34+
const todayFocusCount = await prisma.focusTime.count({
35+
where:{
36+
memberId: Number(memberId),
37+
createdAt:{
38+
gte: new Date(today.getTime() - kstOffset),
39+
lt: new Date(today.getTime() + 24 * 60 * 60 * 1000 - kstOffset),
40+
},
41+
},
42+
});
43+
44+
//오늘 focusTime 없으면 streak는 0
45+
if (todayFocusCount === 0) return 0;
46+
47+
// 연속 기록 일수 계산
48+
const days = Math.floor((today - startDate) / (24 * 60 * 60 * 1000)) + 1;
49+
50+
return days;
3051
}
3152

3253
/**

0 commit comments

Comments
 (0)