Skip to content

Commit 7b7970f

Browse files
committed
feat:월별 유저 알바 조회
1 parent 3b6d540 commit 7b7970f

File tree

4 files changed

+178
-0
lines changed

4 files changed

+178
-0
lines changed

src/DTO/user_alba_schedule_dto.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,17 @@ export interface CreateManualScheduleBody {
1212
memo?: string;
1313
}
1414

15+
export interface UserAlbaScheduleItemDTO {
16+
workplace?: string;
17+
work_date?: string;
18+
work_time?: string;
19+
day_of_week?: user_alba_schedule_day_of_week; // 실제 enum 타입으로 바꿔도 됨
20+
repeat_type?: user_alba_schedule_repeat_type; // 실제 enum 타입으로 바꿔도 됨
21+
repeat_days?: string | null;
22+
hourly_wage?: number;
23+
memo?: string;
24+
}
25+
1526
export interface UpdateManualScheduleBody {
1627
workplace?: string;
1728
work_date?: string;

src/controller/user_alba_schedule_controller.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ import {
1010
Request,
1111
Response,
1212
Security,
13+
Query,
1314
Path,
15+
Get
1416
} from 'tsoa';
1517
import { Request as ExpressRequest } from 'express';
1618
import { TsoaSuccessResponse, TsoaFailResponse } from '../config/response_interface';
@@ -38,6 +40,79 @@ export interface CreateScheduleSuccess {
3840
@Route('user/alba/schedule')
3941
@Tags('User Alba Schedule')
4042
export class UserAlbaScheduleController extends Controller {
43+
44+
/**
45+
* 내 유저 알바 스케줄 목록 조회 (커서/사이즈 없음)
46+
* 예) /user/alba/schedule?month=2026-02
47+
*/
48+
@Security('jwt')
49+
@Get('')
50+
@SuccessResponse(200, '유저 알바 스케줄 목록 조회 성공')
51+
@Response<TsoaFailResponse<any>>(401, 'Unauthorized', {
52+
resultType: 'FAIL' as any,
53+
error: { errorCode: 'EC401', errorMessage: '인증이 필요합니다.', data: null },
54+
success: null,
55+
})
56+
@Response<TsoaFailResponse<any>>(400, 'Bad Request', {
57+
resultType: 'FAIL' as any,
58+
error: { errorCode: 'EC400', errorMessage: '조회 요청이 부적절합니다.', data: null },
59+
success: null,
60+
})
61+
@Response<TsoaFailResponse<any>>(500, 'Internal Server Error', {
62+
resultType: 'FAIL' as any,
63+
error: { errorCode: 'EC500', errorMessage: '서버 오류가 발생했습니다.', data: null },
64+
success: null,
65+
})
66+
public async listMySchedules(
67+
@Request() req: ExpressRequest,
68+
@Query() month?: string, // "YYYY-MM"
69+
): Promise<TsoaSuccessResponse<any>> {
70+
const userUuid = (req as any).user?.id as string;
71+
72+
console.log("token userId =", userUuid);
73+
console.log("month =", month);
74+
75+
const data = await userAlbaScheduleService.listMySchedules(userUuid, { month });
76+
77+
this.setStatus(200);
78+
return new TsoaSuccessResponse(data);
79+
}
80+
81+
/**
82+
* 내 유저 알바 스케줄 단건 조회(상세)
83+
*/
84+
@Security('jwt')
85+
@Get('{userAlbaScheduleId}')
86+
@SuccessResponse(200, '유저 알바 스케줄 단건 조회 성공')
87+
@Response<TsoaFailResponse<any>>(401, 'Unauthorized', {
88+
resultType: 'FAIL' as any,
89+
error: { errorCode: 'EC401', errorMessage: '인증이 필요합니다.', data: null },
90+
success: null,
91+
})
92+
@Response<TsoaFailResponse<any>>(404, 'Not Found', {
93+
resultType: 'FAIL' as any,
94+
error: { errorCode: 'EC404', errorMessage: '스케줄을 찾을 수 없습니다.', data: null },
95+
success: null,
96+
})
97+
@Response<TsoaFailResponse<any>>(500, 'Internal Server Error', {
98+
resultType: 'FAIL' as any,
99+
error: { errorCode: 'EC500', errorMessage: '서버 오류가 발생했습니다.', data: null },
100+
success: null,
101+
})
102+
public async getMySchedule(
103+
@Request() req: ExpressRequest,
104+
@Path() userAlbaScheduleId: string,
105+
): Promise<TsoaSuccessResponse<any>> {
106+
const userUuid = (req as any).user?.id as string;
107+
108+
const schedule = await userAlbaScheduleService.getMyScheduleById(userUuid, userAlbaScheduleId);
109+
110+
this.setStatus(200);
111+
return new TsoaSuccessResponse(schedule);
112+
}
113+
114+
115+
41116
/*
42117
* 유저 알바 스케줄 등록 API
43118
* @param request - Express 요청 객체

src/repository/user_alba_schedule_repository.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,49 @@ export interface CreateUserAlbaScheduleRepoInput {
1919
}
2020

2121
export class UserAlbaScheduleRepository {
22+
23+
async listByUserId(userId: string, opts: { month?: string }) {
24+
const userIdBin = uuidToBin(userId);
25+
26+
return prisma.user_alba_schedule.findMany({
27+
where: {
28+
user_id: userIdBin,
29+
...(opts.month
30+
? {
31+
// work_date가 "YYYY-MM-DD" 문자열로 저장된 전제
32+
work_date: { startsWith: opts.month },
33+
}
34+
: {}),
35+
},
36+
orderBy: [
37+
{ work_date: 'asc' }, // null이면 뒤로 가거나 앞에 올 수 있음(필요하면 null 필터)
38+
{ work_time: 'asc' },
39+
],
40+
});
41+
}
42+
43+
async findDetailByIdAndUserId(userId: string, scheduleId: string) {
44+
const userIdBin = uuidToBin(userId);
45+
const scheduleIdBin = uuidToBin(scheduleId);
46+
47+
return prisma.user_alba_schedule.findFirst({
48+
where: {
49+
user_id: userIdBin,
50+
user_alba_schedule_id: scheduleIdBin,
51+
},
52+
select: {
53+
user_alba_schedule_id: true,
54+
workplace: true,
55+
work_date: true,
56+
work_time: true,
57+
day_of_week: true,
58+
repeat_type: true,
59+
repeat_days: true,
60+
hourly_wage: true,
61+
memo: true,
62+
},
63+
});
64+
}
2265
public async create(userId: string, input: CreateUserAlbaScheduleRepoInput): Promise<Uint8Array> {
2366
const created = await prisma.user_alba_schedule.create({
2467
data: {

src/service/user_alba_schedule_service.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ const workLogRepo = new UserWorkLogRepository();
1414

1515
type RepeatBody = Pick<CreateManualScheduleBody, 'repeat_type' | 'repeat_days'>;
1616

17+
const YM = /^\d{4}-(0[1-9]|1[0-2])$/;
18+
19+
1720
function toDateString(d: Date): string {
1821
// DB Date -> "YYYY-MM-DD"
1922
const y = d.getFullYear();
@@ -34,6 +37,13 @@ function toTimeRangeString(start?: Date | null, end?: Date | null): string | und
3437

3538
const MASK7 = /^[01]{7}$/;
3639

40+
function validateMonth(month?: string) {
41+
if (!month) return;
42+
if (!YM.test(month)) {
43+
throw new CustomError('EC400', 400, 'month는 YYYY-MM 형식이어야 합니다.', null);
44+
}
45+
}
46+
3747
function validateManual(body: RepeatBody) {
3848
const rt = body.repeat_type;
3949
const rd = body.repeat_days;
@@ -132,6 +142,45 @@ function parseScheduleToWorkLogData(workDate?: string, workTime?: string) {
132142
return { workDate: date, startTime, endTime, workMinutes };
133143
}
134144

145+
// 유저 알바 스케줄 서비스 목록 조회
146+
export async function listMySchedules(userId: string, q: { month?: string }) {
147+
validateMonth(q.month);
148+
149+
const rows = await scheduleRepo.listByUserId(userId, { month: q.month });
150+
151+
const schedules = rows.map((s: any) => ({
152+
user_alba_schedule_id: binToUuid(s.user_alba_schedule_id),
153+
workplace: s.workplace ?? null,
154+
work_date: s.work_date ?? null,
155+
work_time: s.work_time ?? null,
156+
day_of_week: s.day_of_week ?? null,
157+
repeat_type: s.repeat_type ?? null,
158+
repeat_days: s.repeat_days ?? null,
159+
hourly_wage: s.hourly_wage ?? null,
160+
memo: s.memo ?? null,
161+
}));
162+
163+
return { schedules };
164+
}
165+
166+
/** 단건 조회 */
167+
export async function getMyScheduleById(userId: string, scheduleId: string) {
168+
const s = await scheduleRepo.findDetailByIdAndUserId(userId, scheduleId);
169+
if (!s) throw new CustomError('EC404', 404, '스케줄을 찾을 수 없습니다.', null);
170+
171+
return {
172+
user_alba_schedule_id: binToUuid(s.user_alba_schedule_id as unknown as Uint8Array),
173+
workplace: s.workplace ?? null,
174+
work_date: s.work_date ?? null,
175+
work_time: s.work_time ?? null,
176+
day_of_week: s.day_of_week ?? null,
177+
repeat_type: s.repeat_type ?? null,
178+
repeat_days: s.repeat_days ?? null,
179+
hourly_wage: s.hourly_wage ?? null,
180+
memo: s.memo ?? null,
181+
};
182+
}
183+
135184
// 유저 수동 입력 스케줄 생성
136185
export async function createManual(
137186
userId: string,

0 commit comments

Comments
 (0)