팀 협업 [노션 링크] : https://bubble-city-3ac.notion.site
탁우현 [개인 Github 링크: https://github.com/WooHyunTak]
안재민 [개인 Github 링크: https://github.com/mini-1018]
강범준 [개인 Github 링크: https://github.com/kangbeomjoon]
-
소개: 이사 소비자와 이사 전문가 매칭 서비스
이사 시장에서는 무분별한 가격 책정과 무책임한 서비스 등으로 인해 정보의 투명성 및 신뢰도가 낮은 문제가 존재합니다. 이러한 문제를 해결하기 위해, 소비자가 원하는 서비스와 주거 정보를 입력하면 이사 전문가들이 견적을 제공하고 사용자가 이를 바탕으로 이사 전문가를 선정할 수 있는 매칭 서비스를 제작합니다. 이를 통해 소비자는 견적과 이사 전문가의 이전 고객들로부터의 후기를 확인하며 신뢰할 수 있는 전문가를 선택할 수 있고, 소비자와 이사 전문가 간의 간편한 매칭이 가능합니다.
-
프로젝트 기간: 2024.11.25 ~ 2025.01.14
- Backend: Express.js, PrismaORM
- Server : Nginx, PM2, Git Actions
- Database: PostgreSQL
- 공통 Tool: Git & Github, Discord, Notion
이사요청 보내기
- 기능 개요: 이 기능은 사용자가 기사로부터 견적서를 받기전에 새로운 이사정보를 등록할 수 있다.
- 구현 내용:
- end-point :
POST /moving-request - request-body :
- end-point :
{
"service": "number", // 1, 2, 3
"movingDate" : "date", //이사하는 날
"pickupAddress" : "string", // 출발지
"dropOffAddress" : "string", // 출발지
}-
Response:
-
201 Create: 사용자 정보 전달```json { "id" : "Number"; "service" : "Number"; "movingDate" : "Date"; "pickupAddress" : "string"; "dropOffAddress" : "string"; } ``` -
400 Bad Request: 유효성 검사 실패```json { "path": "/movingRequest", "method": "POST", "message": "Bad Request";, "data": { "message": "이사 서비스 타입이 올바르지 않습니다." "이사 날짜가 올바르지 않습니다.", "이사 출발지가 올바르지 않습니다.", "이사 도착지가 올바르지 않습니다.", }, "date": "2024-10-11T06:38:15.804Z" } ```
기사페이지 기준 이사요청 목록 조회
-
기능 개요: 이 기능은 기사가 본인의 지정요청과 견적을 보낼수 있는 이사요청 목록을 조회 한다.
-
구현 내용:
- end-point :
GET /moving-request/by-mover - request-query :
"limit": "number" "isDesignated": "boolean" "cursor": "number" "keyword" : "string" // 이사요청과 관계가 있는 기사의 닉네임, 한 줄, 상세 설명을 포함한다. "smallMove" : "boolean" "houseMove" : "boolean" "officeMove" : "boolean" "orderBy" : "stirng" // recent, movingDate "isQuoted" : "boolean" //예시 "?limit=5&cursor=3&isCompleted=ture"
- Response:
-
200 OK: 리스트 조회{ "nextCursor": "", "hasNext": false, "serviceCounts": { "smallMove": 1, "houseMove": 0, "officeMove": 0 }, "requestCounts": { "total": 1, "designated": 0 }, "list": [ { "id": 10, "service": 1, "movingDate": "2024-12-20T00:00:00.000Z", "pickupAddress": "출발지", "dropOffAddress": "도착지", "requestDate": "2024-12-17T11:35:23.718Z", "isConfirmed": false, "name": "김기사", "isDesignated": false } ] } -
404 Not Found: 조건의 맞는 이사 요청 목록이 없음{ "path": "/moving-requests/by-mover", "method": "GET", "message": "Not Found";, "data": { "message": "조건의 맞는 이사요청 목록이 없습니다." }, "date": "2024-10-11T06:38:15.804Z" }
-
- end-point :
고객페이지 기준 이사요청 목록 조회
-
기능 개요: 이 기능은 고객이 본인의 이사요청 목록을 조회 한다.
-
구현 내용:
- end-point :
GET /moving-request/by-mover - request-query :
"**pageSize**": "number" "**pageNum**": "boolean" //예시 "?**pageSize**=5&**pageNum**=3"
- Response:
-
200 OK: 리스트 조회{ "currentPage": 1, "pageSize": 10, "totalPage": 1, "totalCount": 1, "list": [ { "id": 8, "service": 1, "movingDate": "2024-12-12T00:00:00.000Z", "pickupAddress": "출발지", "dropOffAddress": "도착지", "name": "김철수", "requestDate": "2024-12-10T17:55:22.589Z", "isConfirmed": false } ... ] } -
404 Not Found: 조건의 맞는 이사 요청 목록이 없음{ "path": "/moving-requests/by-customer", "method": "GET", "message": "Not Found";, "data": { "message": "조건의 맞는 이사요청 목록이 없습니다." }, "date": "2024-10-11T06:38:15.804Z" }
-
- end-point :
이사요청의 전달된 견적서 조회
-
기능 개요: 이 기능은 해당 Id의 이사요청의 전달된 견적서를 조회 한다.
-
구현 내용:
- end-point :
GET /moving-request/:id/quotes - request-query :
":id": "number" // 이사요청 ID "isCompleted" : "boolean"
- Response:
-
200 OK: 리스트 조회{ "id" : "number"; // 이사요청 ID "list": [ { "id" : "number"; "cost": "number"; "comment": "string"; "service": "number"; "isConfirmed": "boolean"; "mover" : { "id" : "number", "imageUrl" : "string", "nickname" : "string", "career" : "number", "isDesignated" : "boolean", "isFavorite" : "boolean", "reviewCount" : "number", "favoriteCount" : "number", "confirmCount" : "number", "rating" : { "1" : "number", "2" : "number", "3" : "number", "4" : "number", "5" : "number", "average" : "number", "totalCount" : "number", "totalSum" : "number" } }, ... ] } -
404 Not Found: 견적서 목록이 없음{ "path": "/moving-request/:id/quotes", "method": "GET", "message": "Not Found";, "data": { "message": "견적서 목록이 없습니다." }, "date": "2024-10-11T06:38:15.804Z" }
-
- end-point :
고객 페이지 기준 고객의 활성 이사요청의 전달된 대기중 견적서 조회
- 기능 개요: 이 기능은 로그인한 사용자의 활성 이사요청의 대기중인 견적서 목록을 조회한다.
- 구현 내용:
- end-point :
GET /moving-request/pending-quotes - Response:
-
200 OK: 리스트 조회{ "totalCount": 1, "list": [ { "id": 10, "cost": 200000, "comment": "20만원", "isConfirmed": false, "movingRequest": { "service": 2, "movingDate": "2024-12-15T00:00:00.000Z", "pickupAddress": "출발!", "dropOffAddress": "도착!", "requestDate": "2024-12-13T15:08:51.290Z", "isConfirmed": false, "status": "PENDING" }, "mover": { "id": 1, "nickname": "하늘하늘기사", "imageUrl": null, "career": 5, "introduction": "고객님을 위한 안전하고 빠른 이사를 제공합니다.", "services": [ 1, 2 ], "name": "김하늘", "isDesignated": false, "isFavorite": true, "reviewCount": 0, "favoriteCount": 1, "confirmCount": 1, "rating": { "1": 0, "2": 0, "3": 0, "4": 0, "5": 0, "totalCount": 0, "totalSum": 0, "average": 0 } } } ] } -
404 Not Found: 견적서 목록이 없음{ "path": "/moving-request/:id/quotes", "method": "GET", "message": "Not Found";, "data": { "message": "견적서 목록이 없습니다." }, "date": "2024-10-11T06:38:15.804Z" }
-
- end-point :
고객 페이지 기준 고객의 활성 이사요청의 기사 지정하기
- 기능 개요: 이 기능은 로그인한 사용자의 활성 이사요청의 기사 지정
- 구현 내용:
-
end-point :
POST /moving-request/id:/designated -
request-query :
":id": "number" // 이사요청 ID "moverId" : "number" //예시 "moving-requests/6/designated?moverId=4"
-
Response:
-
200 OK: 기사 지정 완료{ "message": "지정 요청 완료", "designateRemain": 1 // 활성중인 이사요청의 남은 지정수 } -
400 Bad Rquest: 활성중인 이사요청이 없음{ "path": "/moving-request/:id/designated", "method": "POST", "message": "Bad Request";, "data": { "message": "일반 견적 요청을 먼저 진행해 주세요." }, "date": "2024-10-11T06:38:15.804Z" } -
400 Bad Rquest: 지정가능 카운트 초과{ "path": "/moving-request/:id/designated", "method": "POST", "message": "Bad Request";, "data": { "message": "지정 요청 가능한 인원이 초과되었습니다. (최대 3명)" }, "date": "2024-10-11T06:38:15.804Z" }
-
-
고객 페이지 기준 고객의 활성 이사요청의 기사 지정취소
- 기능 개요: 이 기능은 로그인한 사용자의 활성 이사요청의 기사 지정 취
- 구현 내용:
-
end-point :
DELETE /moving-request/:id/designated -
request-query :
":id": "number" // 이사요청 ID "moverId" : "number" //예시 "moving-requests/6/designated?moverId=4"
-
Response:
-
200 OK: 기사 지정 취소 완료{ "message": "지정 요청 취소", "designateRemain": 1 // 활성중인 이사요청의 남은 지정수 }
-
-
기사 목록 조회
- 기능 개요: 이 기능은 등록된 기사 목록 조회를 한다.
- 구현 내용:
-
end-point :
GET /mover -
request-query :
"nextCursorId" = "number"; "order" = "string"; // review, career, confirm, rating "limit" = "number"; "keyword" = "string"; "region" = "number"; "service" = "number"; "isFavorite" = "boolean"; //예시 "?order=rating&limit=4&nextCursorId=4"
-
Response:
-
200 OK: 리스트 전{ "nextCursor": "", "hasNext": false, "list": [ { "id": 5, "imageUrl": null, "services": [ 1, 2, 3 ], "nickname": "김기사", "name": "김영수" "career": 2, "regions": [ 82031, 82032 ], "introduction": "잘 하겠습니다", "isDesignated": false, "isFavorite": true, "reviewCount": 0, "favoriteCount": 1, "confirmCount": 0, "rating": { "1": 0, "2": 0, "3": 0, "4": 0, "5": 0, "totalCount": 0, "totalSum": 0, "average": 0 } } ... ] } -
404 Not Found: 조건의 맞는 이사 요청 목록이 없음{ "path": "/movers", "method": "GET", "message": "Not Found";, "data": { "message": "조건에 맞는 기사 목록이 없습니다." }, "date": "2024-10-11T06:38:15.804Z" }
-
-
기사 상세 조회
-
기능 개요: 기사 상세 정보 조회
-
구현 내용:
- end-point :
GET /moving/:id - request-query :
":id": "number" // 기사 ID
- Response:
-
200 OK: 리스트 조회{ "id": 3, "imageUrl": null, "services": [ 1 ], "nickname": "김기사", "name": "김영수", "career": 3, "regions": [ 82041, 82062 ], "introduction": "정확하고 안전한 이사, 믿고 맡겨주세요.", "isDesignated": true, "isFavorite": true, "reviewCount": 0, "favoriteCount": 1, "confirmCount": 0, "rating": { "1": 0, "2": 0, "3": 0, "4": 0, "5": 0, "totalCount": 0, "totalSum": 0, "average": null } } -
404 Not Found: 견적서 목록이 없음{ "path": "/movers/:id", "method": "GET", "message": "Not Found";, "data": { "message": "기사 정보를 찾을 수 없습니다." }, "date": "2024-10-11T06:38:15.804Z" }
-
- end-point :
기사의 프로필 조회
- 기능 개요: 이 기능은 로그인한 기사의 프로필 조회
- 구현 내용:
- end-point :
GET /my-profile - Response:
-
200 OK: 리스트 조회{ "id": 3, "imageUrl": null, "services": [ 1 ], "nickname": "김기사", "name" : "김영수" "career": 3, "regions": [ 82041, 82062 ], "introduction": "정확하고 안전한 이사, 믿고 맡겨주세요.", "isDesignated": true, "isFavorite": true, "reviewCount": 0, "favoriteCount": 1, "confirmCount": 0, "rating": { "1": 0, "2": 0, "3": 0, "4": 0, "5": 0, "totalCount": 0, "totalSum": 0, "average": null } } -
404 Not Found: 조건의 맞는 이사 요청 목록이 없음{ "path": "/my-profile", "method": "GET", "message": "Not Found";, "data": { "message": "기사 정보를 찾을 수 없습니다." }, "date": "2024-10-11T06:38:15.804Z" }
-
- end-point :
기사 찜하기
-
기능 개요: 이 기능은 로그인한 사용자가 해당 기사를 찜한다
-
구현 내용:
- end-point :
GET /:id/favorite - request-query :
":id" : "number" //기사 ID "favorite": "boolean" // true 오면 찜 or false로 보내면 찜 취소 //예시 "movers/5/favorite?favorite=false"
- Response:
-
200 OK: 리스트 조회{ "isFavorite" : "boolean"; "id": "number"; // 찜한 기사 ID } -
404 Not Found: 견적서 목록이 없음{ "path": "/movers/:id/favorite", "method": "POST", "message": "Not Found";, "data": { "message": "기사 정보를 찾을 수 없습니다." }, "date": "2024-10-11T06:38:15.804Z" }
-
- end-point :
고객페이지 견적서 상세 조회
- 기능 개요: 고객페이지 기준의 견적서 상세 조회
- 구현 내용:
-
end-point :
GET /quotes/:id -
request-query :
":id" : "number" //견적 ID
-
Response:
-
200 OK: 리스트 전{ "id": 9, "cost": 150000, "comment": "15만원", "movingRequest": { "service": 1, "movingDate": "2024-12-12T00:00:00.000Z", "pickupAddress": "출발지", "dropOffAddress": "도착지", "requestDate": "2024-12-10T17:55:22.589Z", "isConfirmed": true, "status": "COMPLETED" }, "isConfirmed": true, "mover": { "id": 1, "nickname": "하늘하늘기사", "imageUrl": null, "introduction": "고객님을 위한 안전하고 빠른 이사를 제공합니다.", "services": [ 1, 2 ], "regions": [ 82031, 82032 ], "career": 5, "name": "김하늘", "isDesignated": false, "isFavorite": true, "reviewCount": 0, "favoriteCount": 1, "confirmCount": 1, "rating": { "1": 0, "2": 0, "3": 0, "4": 0, "5": 0, "totalCount": 0, "totalSum": 0, "average": 0 } } } -
404 Not Found: 해당 id의 견적서를 찾지 못 함{ "path": "/quotes/:id", "method": "GET", "message": "Not Found";, "data": { "message": "견적서를 찾을 수 없습니다." }, "date": "2024-10-11T06:38:15.804Z" }
-
-
고객페이지 견적서 확정하기
- 기능 개요: 고객페이지 기준의 전달받은 견적서 확정
- 구현 내용:
-
end-point :
POST /confirmed-quotes/:id -
request-query :
":id" : "number" //견적서 ID
-
Response:
-
200 OK:{ "message": "견적서 확정 완료", "data" : { "id": "number", //확정 견적 테이블의 Id "movingRequest" : "number", //이사요청 Id "quoteId" : "number", //견적서 Id "moverId" : "number", //기사 Id } -
404 Not Found: 사용자의 이사요청을 찾지 못 함{ "path": "/confirmed-quotes/:id", "method": "POST", "message": "Not Found";, "data": { "message": "활성중인 이사요청이 없습니다." }, "date": "2024-10-11T06:38:15.804Z" } -
404 Not Found: 해당 id의 견적서를 찾지 못 함{ "path": "/confirmed-quotes/:id", "method": "POST", "message": "Not Found";, "data": { "message": "견적서를 찾을 수 없습니다." }, "date": "2024-10-11T06:38:15.804Z" }
-
-
- quotes [강범준] : (기사님의) 견적서 보내기
- quotes [강범준] : (기사님의) 견적서 보내기
-
/ (POST)
- Endpoint: POST /quotes
- Description : (기사님의)견적서 보내기
- Reqeust :
- body
{ "movingRequestId": 1, "cost": 150000, "comment": "안전하고 신속한 이사를 약속드립니다." }- Response
- 201 Created
{ "success": true, "message": "견적서가 성공적으로 생성되었습니다.", "data": { "id": 6, "cost": 150000, "comment": "안전하고 신속한 이사를 약속드립니다.", "service": 1, "pickupAddress": "123 Maple St, City A", "dropOffAddress": "456 Oak Rd, City B", "movingDate": "2024-12-05T10:00:00.000Z", "isDesignated": false }
-
- quotes [강범준] : (기사님의) 보낸 견적서 목록 조회
-
quotes [강범준] : (기사님의) 보낸 견적서 목록 조회
-
/(GET)
- Endpoint : GET /quotes/mover
- Description : (기사님의)보낸 견적서 목록 조회
- Reqeust : accessToken
- 200 OK
- Response :
{ "nextCursor": null, "hasNext": false, "list": [ { "id": 1, "service": 1, "isDesignated": false, "name": "김철수", "movingDate": "2024-12-28T10:00:00.000Z", "pickupAddress": "서울시 강남구", "dropOffAddress": "서울시 송파구", "cost": 580000, "isCompleted": true, "isConfirmed": false, "requestDate": "2024-12-23T00:00:00.000Z" },
-
- quotes [강범준] : (기사님의) 견적서 상세 조회
- quotes [강범준] : (기사님의) 견적서 상세 조회
-
/(GET)
- Endpoint : GET /quotes/mover/:quoteId
- Description : (기사님의)견적서 상세 조회
- Reqeust : accessToken
- 200 OK
- Response :
{ "id": 37, "cost": 520000, "comment": "대구 지역 이사 전문가입니다.", "service": 2, "customerName": "이수연", "movingDate": "2024-11-15T10:00:00.000Z", "pickupAddress": "대구시 중구", "dropOffAddress": "대구시 북구", "isDesignated": false, "requestDate": "2024-12-23T00:00:00.000Z" }
-
- quotes [강범준] : (기사님의) 지정 이사 요청 반려
- quotes [강범준] : (기사님의) 지정 이사 요청 반려
-
(POST)
- Endpoint : POST /qoutes/mover/:movingRequestId/reject
- Description : (기사님의) 지정 이사 요청 반려
- Reqeust : accessToken
- 200 OK
- Response
{ "id": 1, "comment": "죄송합니다. 그날 예약이 되어 있습니다" }
-
- quotes [강범준] : (기사님이) 반려한 이사요청 목록 조회
-
quotes [강범준] : (기사님이) 반려한 이사요청 목록 조회
(GET)
- Endpoint : GET /quotes/mover/rejected
- Description : (기사님이) 반려한 이사요청 목록 조회
- Request : accessToken
limit=8&nextCursorId=38
- Response :
{ "nextCursor": null, "hasNext": false, "list": [ { "id": 1, "service": 1, "name": "김철수", "movingDate": "2024-12-28T10:00:00.000Z", "pickupAddress": "서울시 강남구", "dropOffAddress": "서울시 송파구", "requestDate": "2024-12-23T00:00:00.000Z" } ] }
- review [강범준] : (고객이 조회하는)기사의 리뷰 목록 조회
- review [강범준] : (고객이 조회하는)기사의 리뷰 목록 조회
-
(GET)
- Endpoint : GET /reviews/mover/:moverId
- Description : 기사의 리뷰 목록 조회
- Request : accessToken
- 200 OK
- Response :
{ "currentPage": 1, "pageSize": 10, "totalPages": 1, "totalCount": 4, "list": [ { "id": 1, "service": 2, "isDesignated": false, "imageUrl": "", "reviewImageUrl": [], "name": "손", "movingDate": "2024-11-01T00:00:00.000Z", "cost": 400000, "rating": 4, "content": "4점 드리겠습니다.", "createdAt": "2025-01-06T06:08:53.427Z" }, ... }
-
- review [강범준] : (고객이) 작성한 리뷰 목록 조회
- review [강범준] : (고객이) 작성한 리뷰 목록 조회
-
(GET)
- Endpoint : GET /reviews/me
- Description : 내가 작성한 리뷰 목록 조회
- Request :
- 200 OK
- Response :
{ "currentPage": 1, "pageSize": 6, "totalPages": 2, "totalCount": 7, "list": [ { "id": 7, "service": 1, "isDesignated": false, "imageUrl": "www.codeit.com/rv2_image1.svg", "nickname": "최지우", "movingDate": "2024-11-15T10:00:00.000Z", "cost": 480000, "reviewImageUrl":[] "rating": 3, "content": "책상에 파손 부분이 있습니다.", "createdAt": "2024-12-29T14:09:00.488Z" }, ... }
-
- review [강범준] : (고객의) 리뷰 생성하기
- review [강범준] : (고객이) 작성할 수 있는(=작성 가능한) 리뷰 목록 조회
- review [강범준] : (고객이) 작성할 수 있는(=작성 가능한) 리뷰 목록 조회
-
(GET)
- Endpoint : GET /reviews/available
- Description : 내가 작성할 수 있는 리뷰 목록 조회
- Request :
- Response :
{ "currentPage": 1, "totalPages": 1, "totalCount": 6, "list": [ { "confirmedQuoteId": 6, "moverId": 7, "imageUrl": [ "www.codeit.com/rv2_image1.svg", ], "service": 1, "isDesignated": false, "movingDate": "2024-11-15T10:00:00.000Z", "nickname": "최지우", "cost": 480000 }, ... }
-
유저
- 로그인 ( 로컬, 소셜 )
- 회원가입 ( 로컬, 소셜 )
- JWT 토큰 방식
- 프로필 등록/수정
- 사용자 정보 가져오기
알림
- 사용자 알림 조회
├─ .github
│ └─ workflows
│ └─ main.yml
├─ .gitignore
├─ data
│ ├─ customer.ts
│ ├─ mock
│ ├─ mover.ts
│ ├─ movingRequest.ts
│ ├─ region.ts
│ ├─ seed.ts
│ ├─ service.ts
│ └─ user.ts
├─ nodemon.json
├─ package-lock.json
├─ package.json
├─ prisma
│ ├─ migrations
│ │ ├─ 20241127014347_init
│ │ │ └─ migration.sql
│ │ ├─ 20241127021240_
│ │ │ └─ migration.sql
│ │ ├─ 20241127021420_
│ │ │ └─ migration.sql
│ │ ├─ 20241128014440_update_user_table
│ │ │ └─ migration.sql
│ │ ├─ 20241202044806_update
│ │ │ └─ migration.sql
│ │ ├─ 20241205002823_update_moving_requset
│ │ │ └─ migration.sql
│ │ ├─ 20241212064234_add_image_table
│ │ │ └─ migration.sql
│ │ ├─ 20241219083653_update_moving_request
│ │ │ └─ migration.sql
│ │ └─ migration_lock.toml
│ └─ schema.prisma
├─ src
│ ├─ app.ts
│ ├─ config
│ │ └─ cookie.config.ts
│ ├─ controllers
│ │ ├─ authController.ts
│ │ ├─ confirmedQuoteController.ts
│ │ ├─ customerController.ts
│ │ ├─ moverController.ts
│ │ ├─ movingRequestController.ts
│ │ ├─ notificationController.ts
│ │ ├─ oauthController.ts
│ │ ├─ quoteController.ts
│ │ ├─ regionController.ts
│ │ ├─ serviceController.ts
│ │ └─ userController.ts
│ ├─ env.ts
│ ├─ middlewares
│ │ ├─ authMiddleware.ts
│ │ ├─ errorHandler.ts
│ │ ├─ passport.ts
│ │ └─ validations
│ │ ├─ movingRequest.ts
│ │ └─ quote.ts
│ ├─ repositorys
│ │ ├─ confirmedQuoteRepository.ts
│ │ ├─ customerRepository.ts
│ │ ├─ imageRepository.ts
│ │ ├─ moverRepository.ts
│ │ ├─ movingRequestRepository.ts
│ │ ├─ notificationRepository.ts
│ │ ├─ quoteRepository.ts
│ │ ├─ regionRepository.ts
│ │ ├─ serviceRepository.ts
│ │ └─ userRepository.ts
│ ├─ schedules
│ │ ├─ images.ts
│ │ └─ notifications.ts
│ ├─ services
│ │ ├─ authService.ts
│ │ ├─ confirmedQuoteService.ts
│ │ ├─ customerService.ts
│ │ ├─ moverService.ts
│ │ ├─ movingRequestService.ts
│ │ ├─ notificationService.ts
│ │ ├─ oauthService.ts
│ │ ├─ quoteService.ts
│ │ ├─ regionService.ts
│ │ ├─ serviceService.ts
│ │ └─ userService.ts
│ ├─ typings
│ │ └─ env.d.ts
│ └─ utils
│ ├─ asyncHandler.ts
│ ├─ checkBoolean.ts
│ ├─ interfaces
│ │ ├─ customError.ts
│ │ ├─ mover
│ │ │ └─ ratingResult.ts
│ │ └─ movingRequest
│ │ └─ movingRequest.ts
│ ├─ mover
│ │ ├─ getRatingsByMover.ts
│ │ └─ processMoverData.ts
│ ├─ multer.ts
│ ├─ prismaClient.ts
│ ├─ quote
│ │ ├─ processQuoteData.ts
│ │ └─ types.ts
│ ├─ s3.utils.ts
│ ├─ timeGap.utils.ts
│ └─ token.utils.ts
└─ tsconfig.json

