| Phiên bản | 1.4 |
| Base URL | https://api.example.com/v1 |
| Hệ thống | Hệ thống Quản lý Nhân sự |
| Module | RECR - Tuyển dụng |
| Ngày | 2026-08-17 |
| Tác giả | Nam Nguyen |
| SRS liên quan | SRS Tuyển dụng |
| Yêu cầu SRS | Endpoint API |
|---|---|
| FR-003 Duyệt tin tuyển dụng | GET /recr/jobs |
| FR-004 Nộp đơn ứng tuyển | POST /recr/applications |
| FR-007 Tải lên hồ sơ | POST /recr/candidate/resume |
| FR-008 Xem đơn ứng tuyển | GET /recr/hr/applications |
| FR-017 Đăng tin tuyển dụng | POST /recr/employer/jobs |
| FR-037 Quản lý hồ sơ ứng viên | GET /recr/candidate/resumes, POST /recr/candidate/resume, DELETE /recr/candidate/resumes/:id |
| FR-038 Liên kết hồ sơ với đơn ứng tuyển | POST /recr/applications, GET /recr/candidate/applications/:id/resume |
/v1/)user (tự đăng ký), super_admin (chỉ tạo bởi quản trị viên)HR, Candidate, Employer/candidate/, /hr/, /employer/Tất cả các endpoint đều yêu cầu JWT Bearer token trừ khi được đánh dấu là Công khai. JWT chứa vai trò cấp hệ thống của người dùng.
Authorization: Bearer <access_token>
// JWT Payload includes:
{
"sub": "user_id",
"email": "[email protected]",
"role": "user" | "super_admin"
}
// Note: Organization-level roles (HR, Candidate, Employer)
// are managed within organizations, not in the system JWT.
{
"success": true,
"data": { ... },
"meta": {
"requestId": "req-abc-123",
"timestamp": "2026-07-31T10:30:00Z"
}
}
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid input data",
"details": [
{ "field": "email", "message": "Invalid email format" }
]
}
}
| Mã HTTP | Mã lỗi | Mô tả |
|---|---|---|
| 400 | VALIDATION_ERROR | Kiểm tra yêu cầu không thành công |
| 400 | INVALID_ROLE | Vai trò không hợp lệ cho tự đăng ký (super_admin phải được tạo bởi quản trị viên) |
| 401 | UNAUTHORIZED | Thiếu hoặc token không hợp lệ |
| 403 | FORBIDDEN | Không đủ quyền cho vai trò |
| 404 | NOT_FOUND | Không tìm thấy tài nguyên |
| 409 | EMAIL_EXISTS | Email đã được đăng ký (từ module AUTH) |
| 409 | APPLICATION_EXISTS | Ứng viên đã nộp đơn cho vị trí này |
| 409 | RESUME_LINKED | Không thể xóa hồ sơ đã liên kết với đơn ứng tuyển đã nộp |
| 400 | INVALID_FILE | Không tìm thấy tệp trong module FILE hoặc tệp không thuộc danh mục RESUME |
| 400 | INVALID_RESUME_FILE | file_id của hồ sơ không tồn tại hoặc không thuộc về ứng viên |
| 400 | INVALID_FILE_CATEGORY | Chỉ các tệp danh mục RESUME mới có thể liên kết với đơn ứng tuyển |
| 400 | FILE_TOO_LARGE | Kích thước tệp vượt quá giới hạn 10MB |
| 422 | INVALID_STATUS_TRANSITION | Thay đổi trạng thái không hợp lệ |
| 429 | RATE_LIMITED | Quá nhiều yêu cầu |
| 500 | INTERNAL_ERROR | Lỗi máy chủ |
| 503 | SERVICE_UNAVAILABLE | Không thể truy cập module AUTH hoặc dịch vụ downstream |
| 409 | INVITE_EXISTS | Lời mời đang chờ xử lý đã tồn tại cho email+nhà tuyển dụng |
| 400 | INVITE_EXPIRED | Token lời mời đã hết hạn |
| 400 | INVITE_ALREADY_USED | Lời mời đã được chấp nhận |
| 400 | INVITE_REVOKED | Lời mời đã bị thu hồi |
| 400 | CANNOT_INVITE_SELF | Không thể gửi lời mời cho chính mình |
| 400 | INVITE_NOT_PENDING | Chỉ các lời mời đang CHỜ XỬ LÝ mới có thể bị thu hồi |
| 400 | INVITE_NOT_RESENDABLE | Chỉ các lời mời ĐÃ HẾT HẠN hoặc ĐÃ THU HỒI mới có thể gửi lại |
| 400 | EMAIL_MISMATCH | Email người dùng đăng nhập không khớp với lời mời |
| Loại Endpoint | Yêu cầu/phút | Burst |
|---|---|---|
| Endpoint xác thực | 10 | 5 |
| Endpoint ứng viên (vai trò tổ chức) | 60 | 20 |
| Endpoint HR (vai trò tổ chức) | 100 | 30 |
| Endpoint nhà tuyển dụng (vai trò tổ chức) | 60 | 20 |
Lưu ý: Các endpoint này yêu cầu vai trò tổ chức Ứng viên trong ngữ cảnh tổ chức đang hoạt động. Vai trò Ứng viên được gán bởi chủ/quản trị viên tổ chức, không phải khi đăng ký hệ thống.
Duyệt danh sách tin tuyển dụng. Endpoint công khai (không yêu cầu xác thực).
Tham số truy vấn: search, location, salary_min, employment_type, page, limit
Nộp đơn ứng tuyển. Yêu cầu vai trò candidate.
Phụ thuộc liên module: ApplicationService gọi FileService (FILE) để xác minh resume_file_id tồn tại và là tệp danh mục RESUME. resume_file_id được đóng băng (sao chép) vào bản ghi đơn ứng tuyển tại thời điểm nộp.
| Trường | Kiểu | Bắt buộc | Kiểm tra | Mô tả |
|---|---|---|---|---|
job_id | integer | Có | ID vị trí hợp lệ, vị trí phải đang mở | Vị trí tuyển dụng mục tiêu |
cover_letter | string | Không | Tối đa 5000 ký tự | Nội dung thư xin việc |
resume_file_id | string (UUID) | Có | Phải tồn tại trong module FILE, phải là danh mục RESUME, phải thuộc về ứng viên | ID tệp từ module FILE (được đóng băng khi nộp) |
{
"job_id": 1,
"cover_letter": "I am interested in this position...",
"resume_file_id": "550e8400-e29b-41d4-a716-446655440000"
}
{
"success": true,
"data": {
"id": 1,
"job_id": 1,
"candidate_id": 101,
"resume_file_id": "550e8400-e29b-41d4-a716-446655440000",
"cover_letter": "I am interested in this position...",
"status": "submitted",
"applied_at": "2026-08-17T10:00:00Z"
}
}
409 Xung đột -- Đã nộp đơn:
{
"success": false,
"error": {
"code": "APPLICATION_EXISTS",
"message": "Candidate already applied to this job"
}
}
400 Yêu cầu không hợp lệ -- Tệp hồ sơ không hợp lệ:
{
"success": false,
"error": {
"code": "INVALID_RESUME_FILE",
"message": "Resume file not found or does not belong to candidate"
}
}
400 Yêu cầu không hợp lệ -- Sai danh mục tệp:
{
"success": false,
"error": {
"code": "INVALID_FILE_CATEGORY",
"message": "Only RESUME category files can be linked to applications"
}
}
| Quy tắc | Mô tả |
|---|---|
| BR-RECR-08 | Đơn ứng tuyển đóng băng hồ sơ khi nộp: resume_file_id không thể thay đổi sau khi tạo đơn ứng tuyển |
| BR-RECR-09 | Chỉ các tệp danh mục RESUME (từ module FILE) mới có thể liên kết với đơn ứng tuyển |
Xem đơn ứng tuyển của tôi. Yêu cầu vai trò candidate.
Cập nhật hồ sơ. Yêu cầu vai trò candidate.
Tải lên tệp hồ sơ vào danh sách hồ sơ của ứng viên. Yêu cầu vai trò candidate. Tệp được lưu trữ thông qua module FILE.
Phụ thuộc liên module: ProfileService gọi FileService (FILE) để lấy URL tải lên đã ký, sau đó tệp được tải lên module FILE. ProfileService lưu file_id được trả về trong bảng recr_candidate_resumes.
| Trường | Kiểu | Bắt buộc | Kiểm tra | Mô tả |
|---|---|---|---|---|
file_id | string (UUID) | Có | Phải tồn tại trong module FILE, phải là danh mục RESUME | ID tệp được module FILE trả về sau khi tải lên |
{
"file_id": "550e8400-e29b-41d4-a716-446655440000"
}
{
"success": true,
"data": {
"resume_id": 1,
"file_id": "550e8400-e29b-41d4-a716-446655440000",
"file_name": "john_doe_resume.pdf",
"mime_type": "application/pdf",
"file_size": 245760,
"is_primary": false,
"uploaded_at": "2026-08-17T10:00:00Z"
}
}
400 Yêu cầu không hợp lệ -- Tệp không hợp lệ:
{
"success": false,
"error": {
"code": "INVALID_FILE",
"message": "File not found in FILE module or is not a RESUME category file"
}
}
400 Yêu cầu không hợp lệ -- Tệp quá lớn:
{
"success": false,
"error": {
"code": "FILE_TOO_LARGE",
"message": "File size must not exceed 10MB"
}
}
| Quy tắc | Mô tả |
|---|---|
| BR-RECR-07 | Các tệp hồ sơ phải được lưu trữ thông qua module FILE; không cho phép tải tệp trực tiếp lên RECR |
| BR-RECR-09 | Chỉ các tệp danh mục RESUME mới có thể được tải lên làm hồ sơ ứng viên |
Liệt kê tất cả hồ sơ của ứng viên hiện tại. Yêu cầu vai trò candidate.
{
"success": true,
"data": {
"resumes": [
{
"id": 1,
"file_id": "550e8400-e29b-41d4-a716-446655440000",
"file_name": "john_doe_resume.pdf",
"mime_type": "application/pdf",
"file_size": 245760,
"is_primary": true,
"created_at": "2026-08-17T10:00:00Z"
},
{
"id": 2,
"file_id": "660e8400-e29b-41d4-a716-446655440001",
"file_name": "john_doe_resume_v2.docx",
"mime_type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"file_size": 189440,
"is_primary": false,
"created_at": "2026-08-15T14:30:00Z"
}
]
}
}
Xóa hồ sơ khỏi danh sách của ứng viên. Yêu cầu vai trò candidate. Chỉ được phép khi hồ sơ chưa liên kết với đơn ứng tuyển nào đã nộp.
{
"success": true,
"data": {
"message": "Resume deleted successfully"
}
}
409 Xung đột -- Hồ sơ liên kết với đơn ứng tuyển:
{
"success": false,
"error": {
"code": "RESUME_LINKED",
"message": "Cannot delete resume that is linked to a submitted application"
}
}
Lấy URL tải xuống đã ký cho hồ sơ đã đóng băng của đơn ứng tuyển. Yêu cầu vai trò candidate. Ứng viên chỉ có thể xem hồ sơ từ đơn ứng tuyển của chính mình.
Phụ thuộc liên module: ApplicationController gọi FileService (FILE) để tạo URL tải xuống đã ký có thời hạn cho resume_file_id đã đóng băng.
{
"success": true,
"data": {
"download_url": "https://file-storage.example.com/presigned/...",
"expires_at": "2026-08-17T11:00:00Z",
"file_name": "john_doe_resume.pdf"
}
}
Lưu ý: Các endpoint này yêu cầu vai trò tổ chức HR trong ngữ cảnh tổ chức đang hoạt động. Vai trò HR được gán bởi chủ/quản trị viên tổ chức.
Xem đơn ứng tuyển. Yêu cầu vai trò hr.
Tham số truy vấn: job_id, status, page, limit
Cập nhật trạng thái đơn ứng tuyển. Yêu cầu vai trò hr.
Lên lịch phỏng vấn. Yêu cầu vai trò hr.
Yêu cầu:
{
"application_id": 1,
"interviewer_id": 201,
"scheduled_at": "2026-08-05T14:00:00Z",
"duration_minutes": 60,
"location": "Meeting Room A",
"meeting_link": "https://meet.google.com/abc-defg-hij"
}
Gửi đánh giá. Yêu cầu vai trò hr.
Tạo đề nghị tuyển dụng. Yêu cầu vai trò hr.
Xem quy trình tuyển dụng. Yêu cầu vai trò hr.
Tạo báo cáo. Yêu cầu vai trò hr.
Lưu ý: Các endpoint này yêu cầu vai trò tổ chức Nhà tuyển dụng trong ngữ cảnh tổ chức đang hoạt động. Vai trò Nhà tuyển dụng thường được gán cho chủ/quản trị viên tổ chức tạo tin tuyển dụng.
Tạo tin tuyển dụng. Yêu cầu vai trò employer.
Yêu cầu:
{
"title": "Senior Software Engineer",
"description": "We are looking for a senior engineer...",
"department": "Engineering",
"location": "Ho Chi Minh City",
"salary_min": 20000000,
"salary_max": 40000000,
"employment_type": "full_time",
"requirements": [
{ "type": "skill", "description": "5+ years Node.js", "is_mandatory": true }
]
}
Liệt kê tin tuyển dụng của tôi. Yêu cầu vai trò employer.
Cập nhật tin tuyển dụng. Yêu cầu vai trò employer.
Xem người nộp đơn. Yêu cầu vai trò employer.
Gửi để HR phê duyệt. Yêu cầu vai trò employer.
Xem phân tích. Yêu cầu vai trò employer.
Tất cả các endpoint danh sách hỗ trợ phân trang offset.
GET /recr/jobs?page=2&limit=20
| Sự kiện | Hướng | Giao thức | Mô tả |
|---|---|---|---|
| user.registered | Đi | RabbitMQ | Người dùng mới đăng ký với vai trò hệ thống user |
| application.submitted | Đi | RabbitMQ | Ứng viên nộp đơn ứng tuyển |
| interview.scheduled | Đi | RabbitMQ | HR lên lịch phỏng vấn |
| offer.sent | Đi | RabbitMQ | HR gửi thư đề nghị tuyển dụng |
| candidate.hired | Đi | RabbitMQ | Ứng viên chấp nhận đề nghị |
| job.submitted_for_approval | Đi | RabbitMQ | Nhà tuyển dụng gửi tin tuyển dụng để phê duyệt |
| file.deleted | Đến | RabbitMQ | Module FILE thông báo cho RECR khi tệp bị xóa; RECR dọn dẹp các tham chiếu trong recr_candidate_resumes và recr_applications |
| invitation.created | Đi | RabbitMQ | Lời mời được tạo, gửi thông báo email |
| invitation.accepted | Đi | RabbitMQ | Lời mời được chấp nhận, kích hoạt module AUTH |
| Ngày | Phiên bản | Thay đổi |
|---|---|---|
| 2026-07-31 | 1.0.0 | Phiên bản đầu tiên với 3 vai trò: Ứng viên, HR, Nhà tuyển dụng |
| 2026-08-12 | 1.1.0 | API đăng ký chi tiết: request/response riêng biệt cho Ứng viên và Nhà tuyển dụng, quy tắc kiểm tra, phản hồi lỗi, quy tắc kinh doanh, luồng liên module |
| 2026-08-17 | 1.2.0 | Hệ thống vai trò hai cấp mới: vai trò hệ thống (user, super_admin) và vai trò tổ chức (HR, Candidate, Employer). Đăng ký tạo người dùng với vai trò user. JWT chứa vai trò hệ thống. Vai trò tổ chức được quản lý trong tổ chức. |
| 2026-08-17 | 1.3.0 | Tích hợp module FILE: Tải lên hồ sơ thông qua module FILE với tham chiếu file_id. Các endpoint mới: GET /recr/candidate/resumes, DELETE /recr/candidate/resumes/:id, GET /recr/candidate/applications/:id/resume. POST /recr/applications hiện yêu cầu resume_file_id. Thêm quy tắc kinh doanh BR-RECR-07, BR-RECR-08, BR-03-RECR-09. Xử lý sự kiện file.deleted đến. |
| 2026-08-17 | 1.4.0 | Tính năng INVITE được tích hợp như tính năng module RECR. Các endpoint mới: POST /recr/invitations/send, GET /recr/invitations/verify, POST /recr/invitations/accept, GET /recr/invitations, DELETE /recr/invitations/:id, POST /recr/invitations/:id/resend. Bảng: recr_invitations. Sự kiện: invitation.created, invitation.accepted. |