Skip to content

Commit 04c21be

Browse files
authored
Merge pull request #57 from Jinacker/feat/ALBA_56
[FIX] (홈화면) 빠른 알바 일정 추가 후 홈 오늘 근무 리스트 미노출 문제 해결
2 parents 2081e0c + 5b397a0 commit 04c21be

6 files changed

Lines changed: 219 additions & 10 deletions

File tree

src/repository/schedule_repository.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,57 @@ class ScheduleRepository {
3232
});
3333
}
3434

35+
/**
36+
* 일정 생성 + work_log 동시 생성 (트랜잭션)
37+
* 홈 리스트에 노출되도록 user_work_log도 함께 생성
38+
*/
39+
async createScheduleWithWorkLog(
40+
scheduleData: {
41+
user_id: Uint8Array;
42+
workplace: string;
43+
work_date: string;
44+
work_time: string;
45+
hourly_wage: number;
46+
memo?: string;
47+
},
48+
workLogData: {
49+
workDate: Date;
50+
startTime: Date | null;
51+
endTime: Date | null;
52+
workMinutes: number | null;
53+
},
54+
) {
55+
return await prisma.$transaction(async (tx) => {
56+
// 1. 일정 생성
57+
const schedule = await tx.user_alba_schedule.create({
58+
data: {
59+
user_id: scheduleData.user_id as Uint8Array<ArrayBuffer>,
60+
workplace: scheduleData.workplace,
61+
work_date: scheduleData.work_date,
62+
work_time: scheduleData.work_time,
63+
hourly_wage: scheduleData.hourly_wage,
64+
memo: scheduleData.memo || null,
65+
repeat_type: 'none',
66+
},
67+
});
68+
69+
// 2. work_log 생성 (schedule_id 연결)
70+
await tx.user_work_log.create({
71+
data: {
72+
user_id: scheduleData.user_id as Uint8Array<ArrayBuffer>,
73+
user_alba_schedule_id: schedule.user_alba_schedule_id,
74+
work_date: workLogData.workDate,
75+
start_time: workLogData.startTime,
76+
end_time: workLogData.endTime,
77+
work_minutes: workLogData.workMinutes,
78+
status: 'scheduled',
79+
},
80+
});
81+
82+
return schedule;
83+
});
84+
}
85+
3586
/**
3687
* 일정 ID로 일정 조회
3788
* @param scheduleId - 일정 ID (Binary UUID)

src/repository/user_alba_schedule_repository.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,53 @@ export class UserAlbaScheduleRepository {
4444
return created.user_alba_schedule_id;
4545
}
4646

47+
/**
48+
* 스케줄 생성 + work_log 동시 생성 (트랜잭션)
49+
* 수동 일정 생성 시 홈 리스트에 노출되도록 user_work_log도 함께 생성
50+
*/
51+
public async createWithWorkLog(
52+
userId: string,
53+
input: CreateUserAlbaScheduleRepoInput,
54+
workLogData: { workDate: Date; startTime: Date | null; endTime: Date | null; workMinutes: number | null },
55+
): Promise<Uint8Array> {
56+
const userIdBin = uuidToBin(userId);
57+
58+
const schedule = await prisma.$transaction(async (tx) => {
59+
// 1. 일정 생성
60+
const created = await tx.user_alba_schedule.create({
61+
data: {
62+
user_id: userIdBin,
63+
workplace: input.workplace ?? null,
64+
work_date: input.work_date ?? null,
65+
work_time: input.work_time ?? null,
66+
day_of_week: input.day_of_week ?? null,
67+
repeat_type: input.repeat_type ?? null,
68+
repeat_days: input.repeat_days ?? null,
69+
hourly_wage: input.hourly_wage ?? null,
70+
memo: input.memo ?? null,
71+
},
72+
select: { user_alba_schedule_id: true },
73+
});
74+
75+
// 2. work_log 생성 (schedule_id 연결)
76+
await tx.user_work_log.create({
77+
data: {
78+
user_id: userIdBin,
79+
user_alba_schedule_id: created.user_alba_schedule_id,
80+
work_date: workLogData.workDate,
81+
start_time: workLogData.startTime,
82+
end_time: workLogData.endTime,
83+
work_minutes: workLogData.workMinutes,
84+
status: 'scheduled',
85+
},
86+
});
87+
88+
return created;
89+
});
90+
91+
return schedule.user_alba_schedule_id;
92+
}
93+
4794
public async updateByIdAndUserId(
4895
userId: string,
4996
scheduleId: string,

src/repository/work_log_repository.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,8 @@ class WorkLogRepository {
102102
{
103103
user_work_log_id: Uint8Array;
104104
user_id: Uint8Array;
105-
alba_id: Uint8Array;
105+
alba_id: Uint8Array | null;
106+
user_alba_schedule_id: Uint8Array | null;
106107
work_date: Date | null;
107108
start_time: Date | null;
108109
end_time: Date | null;
@@ -113,7 +114,7 @@ class WorkLogRepository {
113114
store: {
114115
store_name: string | null;
115116
};
116-
};
117+
} | null;
117118
}[]
118119
> {
119120
// 날짜 범위 설정 (해당 날짜의 00:00:00 ~ 23:59:59)

src/service/schedule_service.ts

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,22 @@ class ScheduleService {
2626
data.hourlyWage,
2727
);
2828

29-
// 3. 데이터베이스에 일정 저장
30-
const schedule = await ScheduleRepository.createSchedule({
29+
// 3. work_log용 시간 데이터 파싱
30+
const workLogData = this.parseWorkLogData(data.workDate, data.startTime, data.endTime);
31+
32+
// 4. 데이터베이스에 일정 + work_log 저장 (트랜잭션)
33+
const scheduleData = {
3134
user_id: userId,
3235
workplace: data.workplace,
3336
work_date: data.workDate,
3437
work_time: workTime,
3538
hourly_wage: data.hourlyWage,
3639
memo: data.memo,
37-
});
40+
};
3841

39-
// 4. DTO 형식으로 반환
42+
const schedule = await ScheduleRepository.createScheduleWithWorkLog(scheduleData, workLogData);
43+
44+
// 5. DTO 형식으로 반환
4045
return {
4146
scheduleId: this.bufferToUuid(schedule.user_alba_schedule_id),
4247
workplace: schedule.workplace || '',
@@ -48,6 +53,33 @@ class ScheduleService {
4853
};
4954
}
5055

56+
/**
57+
* 일정 데이터를 work_log용 Date 객체로 변환
58+
*/
59+
private parseWorkLogData(
60+
workDate: string,
61+
startTime: string,
62+
endTime: string,
63+
): { workDate: Date; startTime: Date | null; endTime: Date | null; workMinutes: number | null } {
64+
// KST(+09:00) 오프셋을 명시하여 Prisma UTC 변환 시 날짜가 밀리지 않도록 처리
65+
const date = new Date(workDate + 'T00:00:00+09:00');
66+
67+
const [sh, sm] = startTime.split(':').map(Number);
68+
const start = new Date(workDate + `T${String(sh).padStart(2, '0')}:${String(sm).padStart(2, '0')}:00+09:00`);
69+
70+
const [eh, em] = endTime.split(':').map(Number);
71+
const end = new Date(workDate + `T${String(eh).padStart(2, '0')}:${String(em).padStart(2, '0')}:00+09:00`);
72+
73+
// 야간 근무 (종료 시간이 시작 시간보다 이전)
74+
if (end <= start) {
75+
end.setDate(end.getDate() + 1);
76+
}
77+
78+
const workMinutes = Math.round((end.getTime() - start.getTime()) / 60000);
79+
80+
return { workDate: date, startTime: start, endTime: end, workMinutes };
81+
}
82+
5183
/**
5284
* 예상 금액 계산
5385
* 시작 시간과 종료 시간을 기반으로 근무 시간을 계산하고 시급을 곱함

src/service/user_alba_schedule_service.ts

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,50 @@ function validateManual(body: CreateManualScheduleBody) {
4545
}
4646
}
4747

48+
/**
49+
* work_time("09:00-18:00")과 work_date("2026-02-10")를 파싱하여
50+
* user_work_log에 필요한 Date 객체를 생성
51+
*/
52+
function parseScheduleToWorkLogData(workDate?: string, workTime?: string) {
53+
if (!workDate) return null;
54+
55+
// KST(+09:00) 오프셋을 명시하여 Prisma UTC 변환 시 날짜가 밀리지 않도록 처리
56+
const date = new Date(workDate + 'T00:00:00+09:00');
57+
58+
let startTime: Date | null = null;
59+
let endTime: Date | null = null;
60+
let workMinutes: number | null = null;
61+
62+
if (workTime) {
63+
const [startStr, endStr] = workTime.split('-');
64+
if (startStr) {
65+
const [sh, sm] = startStr.split(':').map(Number);
66+
startTime = new Date(workDate + `T${String(sh).padStart(2, '0')}:${String(sm).padStart(2, '0')}:00+09:00`);
67+
}
68+
if (endStr) {
69+
const [eh, em] = endStr.split(':').map(Number);
70+
endTime = new Date(workDate + `T${String(eh).padStart(2, '0')}:${String(em).padStart(2, '0')}:00+09:00`);
71+
// 야간 근무 (종료 시간이 시작 시간보다 이전)
72+
if (endTime <= startTime!) {
73+
endTime.setDate(endTime.getDate() + 1);
74+
}
75+
}
76+
if (startTime && endTime) {
77+
workMinutes = Math.round((endTime.getTime() - startTime.getTime()) / 60000);
78+
}
79+
}
80+
81+
return { workDate: date, startTime, endTime, workMinutes };
82+
}
83+
4884
// 유저 수동 입력 스케줄 생성
4985
export async function createManual(
5086
userId: string,
5187
body: CreateManualScheduleBody,
5288
): Promise<string> {
5389
validateManual(body);
5490

55-
const idBin = await scheduleRepo.create(userId, {
91+
const scheduleInput = {
5692
workplace: body.workplace,
5793
work_date: body.work_date,
5894
work_time: body.work_time,
@@ -61,7 +97,17 @@ export async function createManual(
6197
repeat_days: body.repeat_days,
6298
hourly_wage: body.hourly_wage,
6399
memo: body.memo,
64-
});
100+
};
101+
102+
// work_date가 있으면 work_log도 함께 생성 (홈 리스트 노출용)
103+
const workLogData = parseScheduleToWorkLogData(body.work_date, body.work_time);
104+
105+
let idBin: Uint8Array;
106+
if (workLogData) {
107+
idBin = await scheduleRepo.createWithWorkLog(userId, scheduleInput, workLogData);
108+
} else {
109+
idBin = await scheduleRepo.create(userId, scheduleInput);
110+
}
65111

66112
return binToUuid(idBin);
67113
}

src/service/work_log_service.ts

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import WorkLogRepository from '../repository/work_log_repository';
2+
import prisma from '../config/prisma';
23
import {
34
TodayScheduleResponseDto,
45
TodayWorkListResponseDto,
@@ -35,6 +36,22 @@ class WorkLogService {
3536
// Repository에서 오늘 날짜의 work_log 가져오기
3637
const workLogs = await WorkLogRepository.findWorkLogsByDate(userId, today);
3738

39+
// alba_posting이 없는 work_log의 schedule 정보를 일괄 조회
40+
const scheduleIds = workLogs
41+
.filter((log) => !log.alba_posting && log.user_alba_schedule_id)
42+
.map((log) => log.user_alba_schedule_id as Uint8Array<ArrayBuffer>);
43+
44+
const scheduleMap = new Map<string, { workplace: string | null; hourly_wage: number | null }>();
45+
if (scheduleIds.length > 0) {
46+
const schedules = await prisma.user_alba_schedule.findMany({
47+
where: { user_alba_schedule_id: { in: scheduleIds } },
48+
select: { user_alba_schedule_id: true, workplace: true, hourly_wage: true },
49+
});
50+
for (const s of schedules) {
51+
scheduleMap.set(Buffer.from(s.user_alba_schedule_id).toString('hex'), s);
52+
}
53+
}
54+
3855
// DTO 형식으로 변환
3956
const scheduleDtos: TodayScheduleResponseDto[] = workLogs.map((log) => {
4057
const { startTime, endTime, workHours } = this.parseWorkLogTime(
@@ -43,15 +60,30 @@ class WorkLogService {
4360
log.work_minutes,
4461
);
4562

46-
const hourlyWage = log.alba_posting.hourly_rate || 0;
63+
// alba_posting이 있으면 사용, 없으면 schedule에서 fallback
64+
let workplace = '';
65+
let hourlyWage = 0;
66+
67+
if (log.alba_posting) {
68+
workplace = log.alba_posting.store.store_name || '';
69+
hourlyWage = log.alba_posting.hourly_rate || 0;
70+
} else if (log.user_alba_schedule_id) {
71+
const key = Buffer.from(log.user_alba_schedule_id).toString('hex');
72+
const schedule = scheduleMap.get(key);
73+
if (schedule) {
74+
workplace = schedule.workplace || '';
75+
hourlyWage = schedule.hourly_wage || 0;
76+
}
77+
}
78+
4779
const totalWage = Math.round(hourlyWage * workHours);
4880
const status = log.status || 'scheduled';
4981

5082
return {
5183
workLogId: bufferToUuid(log.user_work_log_id),
5284
status,
5385
statusLabel: STATUS_LABELS[status] || '알 수 없음',
54-
workplace: log.alba_posting.store.store_name || '',
86+
workplace,
5587
startTime,
5688
endTime,
5789
workHours,

0 commit comments

Comments
 (0)