diff --git a/README.md b/README.md index a0f7ca29..47206073 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ ## πŸ“Š ν”„λ‘œμ νŠΈ ν˜„ν™© μš”μ•½ -> λ§ˆμ§€λ§‰ 뢄석: 2026-01-26 | **전체 μ™„λ£Œμœ¨: 97%** +> λ§ˆμ§€λ§‰ 뢄석: 2026-01-31 | **전체 μ™„λ£Œμœ¨: 97%** | ν•­λͺ© | ν˜„ν™© | 상세 | |------|:----:|------| @@ -907,8 +907,8 @@ SUPABASE_SERVICE_KEY=μ„œλΉ„μŠ€_ν‚€ # (선택사항) | ν•­λͺ© | μƒνƒœ | |------|------| -| **버전** | 1.7.0 | -| **λ§ˆμ§€λ§‰ μ—…λ°μ΄νŠΈ** | 2026-01-26 | +| **버전** | 1.7.1 | +| **λ§ˆμ§€λ§‰ μ—…λ°μ΄νŠΈ** | 2026-01-31 | | **μœ μ§€λ³΄μˆ˜** | ν™œλ°œνžˆ μ§„ν–‰ 쀑 | | **λ¬Έμ„œν™”** | μ™„λ£Œ | | **ν…ŒμŠ€νŠΈ 컀버리지** | μ§„ν–‰ 쀑 | @@ -916,7 +916,7 @@ SUPABASE_SERVICE_KEY=μ„œλΉ„μŠ€_ν‚€ # (선택사항) ## πŸ“Š 개발 ν˜„ν™© (Development Status) -> λ§ˆμ§€λ§‰ 뢄석: 2026-01-26 | 전체 μ™„λ£Œμœ¨: **97%** +> λ§ˆμ§€λ§‰ 뢄석: 2026-01-31 | 전체 μ™„λ£Œμœ¨: **97%** ### 🎯 전체 개발 μ§„ν–‰λ₯  @@ -994,6 +994,18 @@ SUPABASE_SERVICE_KEY=μ„œλΉ„μŠ€_ν‚€ # (선택사항) ## πŸ”„ λ³€κ²½ 이λ ₯ +### v1.7.1 (2026-01-31) +- πŸ“ **ν”„λ‘œμ νŠΈ 제λͺ© 15자 μ œν•œ μΆ”κ°€** + - `POST /api/projects` - ν”„λ‘œμ νŠΈ 생성 μ‹œ 제λͺ© 15자 μ œν•œ + - `PUT /api/projects/:id` - ν”„λ‘œμ νŠΈ μˆ˜μ • μ‹œ 제λͺ© 15자 μ œν•œ + - `POST /api/projects/from-recruitment/:id` - ν‚₯μ˜€ν”„ μ‹œ 제λͺ© 15자 μ œν•œ +- ⭐ **평가 API λ‹΄λ‹Ή 업무 ν•„λ“œ μΆ”κ°€** + - `GET /api/evaluations/given` 응닡에 `target_member_task` ν•„λ“œ μΆ”κ°€ + - 평가 λŒ€μƒμžμ˜ ν”„λ‘œμ νŠΈ λ‚΄ λ‹΄λ‹Ή 업무 ν‘œμ‹œ 지원 +- πŸ“ **Swagger λ¬Έμ„œ μ—…λ°μ΄νŠΈ** + - ν”„λ‘œμ νŠΈ 제λͺ© 15자 μ œν•œ λͺ…μ‹œ + - 평가 API 응닡 μŠ€ν‚€λ§ˆ 상세화 + ### v1.7.0 (2026-01-26) - πŸ“± **SMS 인증 μ‹œμŠ€ν…œ κ΅¬ν˜„** - Solapi ν†΅ν•©μœΌλ‘œ SMS λ°œμ†‘ diff --git a/src/controllers/projectController.js b/src/controllers/projectController.js index 89e7b664..7056edf9 100644 --- a/src/controllers/projectController.js +++ b/src/controllers/projectController.js @@ -6,6 +6,11 @@ const createProject = async (req, res) => { // JWTμ—μ„œ user_id κ°€μ Έμ˜€κΈ° (authMiddlewareκ°€ μ„€μ •) const user_id = req.user.userId; + // ν”„λ‘œμ νŠΈ 제λͺ© 15자 μ œν•œ + if (req.body.title && req.body.title.length > 15) { + return res.status(400).json({ error: "ν”„λ‘œμ νŠΈ 제λͺ©μ€ 15자 이내여야 ν•©λ‹ˆλ‹€." }); + } + // ν”„λ‘œμ νŠΈ 생성 (recruitment_id λΆˆν•„μš”) const newProject = await Project.create({ ...req.body, @@ -87,6 +92,11 @@ const updateProject = async (project_id, updateData) => { const project = await Project.findByPk(project_id); if (!project) throw new Error("ν”„λ‘œμ νŠΈλ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€."); + // ν”„λ‘œμ νŠΈ 제λͺ© 15자 μ œν•œ + if (updateData.title && updateData.title.length > 15) { + throw new Error("ν”„λ‘œμ νŠΈ 제λͺ©μ€ 15자 이내여야 ν•©λ‹ˆλ‹€."); + } + // statusκ°€ "COMPLETED"일 경우, end_dateκ°€ μ—†μœΌλ©΄ ν˜„μž¬ λ‚ μ§œλ‘œ μ„€μ • if (updateData.status === "COMPLETED" && !project.end_date) { updateData.end_date = new Date(); @@ -319,6 +329,11 @@ const createProjectFromRecruitment = async (req, res) => { return res.status(400).json({ error: "ν”„λ‘œμ νŠΈ 제λͺ©μ€ ν•„μˆ˜μž…λ‹ˆλ‹€." }); } + if (title.length > 15) { + await transaction.rollback(); + return res.status(400).json({ error: "ν”„λ‘œμ νŠΈ 제λͺ©μ€ 15자 이내여야 ν•©λ‹ˆλ‹€." }); + } + if (!memberUserIds || !Array.isArray(memberUserIds) || memberUserIds.length === 0) { await transaction.rollback(); return res.status(400).json({ error: "ν”„λ‘œμ νŠΈ 멀버λ₯Ό μ„ νƒν•΄μ£Όμ„Έμš”." }); diff --git a/swagger.yaml b/swagger.yaml index c355082e..3e863af8 100644 --- a/swagger.yaml +++ b/swagger.yaml @@ -2633,6 +2633,9 @@ paths: post: tags: [Project] summary: ν”„λ‘œμ νŠΈ 생성 + description: | + μƒˆ ν”„λ‘œμ νŠΈλ₯Ό μƒμ„±ν•©λ‹ˆλ‹€. + - ν”„λ‘œμ νŠΈ 제λͺ©μ€ **15자 이내**둜 μ œν•œλ©λ‹ˆλ‹€. security: - bearerAuth: [] requestBody: @@ -2641,10 +2644,12 @@ paths: application/json: schema: type: object - required: [introduction] + required: [title] properties: - introduction: + title: type: string + maxLength: 15 + description: ν”„λ‘œμ νŠΈ 제λͺ© (μ΅œλŒ€ 15자) description: type: string start_date: @@ -2875,7 +2880,10 @@ paths: post: tags: [Project] summary: λͺ¨μ§‘κΈ€μ—μ„œ ν”„λ‘œμ νŠΈ 생성 (ν‚₯μ˜€ν”„) - description: λͺ¨μ§‘글을 ν”„λ‘œμ νŠΈλ‘œ μ „ν™˜ν•©λ‹ˆλ‹€. ν”„λ‘œμ νŠΈ 제λͺ©, 닀짐, μ§„ν–‰κΈ°κ°„, μ„ νƒλœ 멀버λ₯Ό μž…λ ₯λ°›μŠ΅λ‹ˆλ‹€. + description: | + λͺ¨μ§‘글을 ν”„λ‘œμ νŠΈλ‘œ μ „ν™˜ν•©λ‹ˆλ‹€. + ν”„λ‘œμ νŠΈ 제λͺ©, 닀짐, μ§„ν–‰κΈ°κ°„, μ„ νƒλœ 멀버λ₯Ό μž…λ ₯λ°›μŠ΅λ‹ˆλ‹€. + - ν”„λ‘œμ νŠΈ 제λͺ©μ€ **15자 이내**둜 μ œν•œλ©λ‹ˆλ‹€. security: - bearerAuth: [] parameters: @@ -2897,8 +2905,9 @@ paths: properties: title: type: string - description: ν”„λ‘œμ νŠΈ 제λͺ© (λͺ¨μ§‘κΈ€ 제λͺ©κ³Ό 별도) - example: "νŒ€μ΄νƒ€μΉ΄ MVP 개발" + maxLength: 15 + description: ν”„λ‘œμ νŠΈ 제λͺ© (μ΅œλŒ€ 15자) + example: "νŒ€μ΄νƒ€μΉ΄ MVP" resolution: type: string description: 닀짐 λ‚΄μš© @@ -6792,8 +6801,71 @@ paths: type: array items: type: object + properties: + review_id: + type: string + format: uuid + target_member_id: + type: string + format: uuid + target_member_name: + type: string + description: 평가 λŒ€μƒμž 이름 + target_member_task: + type: string + nullable: true + description: 평가 λŒ€μƒμžμ˜ λ‹΄λ‹Ή 업무 + participation: + type: integer + minimum: 1 + maximum: 5 + communication: + type: integer + minimum: 1 + maximum: 5 + responsibility: + type: integer + minimum: 1 + maximum: 5 + cooperation: + type: integer + minimum: 1 + maximum: 5 + ability: + type: integer + minimum: 1 + maximum: 5 + overall: + type: integer + minimum: 1 + maximum: 5 + comment: + type: string + evaluated_at: + type: string + format: date-time total: type: integer + examples: + success: + summary: 평가 쑰회 성곡 + value: + success: true + data: + given_evaluations: + - review_id: "550e8400-e29b-41d4-a716-446655440000" + target_member_id: "550e8400-e29b-41d4-a716-446655440001" + target_member_name: "홍길동" + target_member_task: "λ°±μ—”λ“œ 개발" + participation: 5 + communication: 4 + responsibility: 5 + cooperation: 4 + ability: 5 + overall: 5 + comment: "μ—΄μ‹¬νžˆ μ°Έμ—¬ν–ˆμŠ΅λ‹ˆλ‹€" + evaluated_at: "2024-01-15T10:30:00Z" + total: 1 '400': description: projectId νŒŒλΌλ―Έν„° λˆ„λ½ '401':