04 · Kỹ thuật
Phân tích BA cho dev
Đặc tả kỹ thuật để hiện thực bộ rule: mô hình dữ liệu, thuật toán kèm pseudocode, API và edge case. Trang Rule điều phối là nguồn chân lý nghiệp vụ; trang này tập trung cách hiện thực.
Mô hình dữ liệu
Các thực thể cốt lõi. Kiểu dữ liệu mang tính gợi ý, đội dev điều chỉnh theo stack thực tế.
Employee (Nhân viên)
| Trường | Kiểu | Ghi chú |
employee_id | string (PK) | Mã nhân viên |
areas | array<string> | Khu vực đăng ký được phép làm |
skills | set<skill_id> | Tập kỹ năng (binary — có mặt = có kỹ năng) |
performance_score | decimal | Điểm hiệu suất tổng hợp 0–5 |
status | enum | available / busy / on_leave |
monthly_service_pay | decimal | Lương dịch vụ lũy kế tháng (tính band) |
Service & Step
| Trường | Kiểu | Ghi chú |
service_id | string (PK) | |
type | enum | regular / branch_opening |
urgency | enum | normal / urgent |
deadline | datetime | Tính tải & cảnh báo trễ |
steps[] | array<Step> | Thứ tự = thứ tự thực hiện |
step.skill_id | skill_id | Kỹ năng bắt buộc của bước |
step.std_minutes | int | Định biên thời gian |
step.unit_price | decimal | Đơn giá bước |
step.crew_size | int | Số người cần (1 hoặc 2) |
step.split_ratio | array<decimal> | Tỉ lệ chia khi crew_size=2, vd [0.6,0.4] |
Skill & Assignment
| Trường | Kiểu | Ghi chú |
skill.is_unique | bool | True nếu ≤1 người sở hữu → tách flow + cảnh báo HR |
assignment.flag | enum | auto_in_band / auto_out_of_band / self_picked / urgent |
assignment.status | enum | assigned / in_progress / done / returned |
assignment.cluster | array<int> | Các step thuộc cùng cụm |
Thuật toán điều phối
Phân loại đầu vào
function dispatch(service):
if service.urgency == 'urgent':
return dispatchUrgent(service)
if service.type == 'branch_opening':
return dispatchBranchProject(service)
return dispatchRegular(service) // việc lẻ
Việc lẻ — gom cụm
function buildCluster(emp, service, startSeq):
cluster = []; total = 0
for step in service.steps[startSeq:]:
if step.skill_id not in emp.skills: break // thiếu KN
if total + step.std_minutes > session_cap: break // trần buổi
cluster.add(step); total += step.std_minutes
return cluster, total
Việc lẻ — chọn người
function dispatchRegular(service):
startSeq = firstUnassignedStep(service)
skill = service.steps[startSeq].skill_id
// B1: lọc cứng
cands = employees.filter(e =>
skill in e.skills
AND service.branch.area in e.areas
AND e.status == 'available'
AND remainingSessionTime(e) > 0)
if cands.isEmpty(): // B4 van an toàn
cands = anyAvailableWithSkill(skill, service.branch.area)
flag = 'auto_out_of_band'
else:
flag = 'auto_in_band'
// B2-B3: chấm điểm (band qua w4, KHÔNG phải bộ lọc)
minPay = min(e.monthly_service_pay for e in allActive)
for e in cands:
cluster, mins = buildCluster(e, service, startSeq)
payGap = e.monthly_service_pay - minPay
e.score = w1*e.performance_score
+ w2*len(e.skills)
- w3*currentLoadHours(e)
+ w4*max(0, band_limit - payGap)/100000
+ clusterBonus(cluster)
winner = argmax(cands, by=score)
return createAssignment(winner, cluster, flag)
Việc khẩn & dự án
function dispatchUrgent(service):
target = pick(employeesOnSite(service.branch), by=nearest)
// KHÔNG cắt cụm; giao thêm, cho vượt trần buổi
a = createAssignment(target, service, flag='urgent',
allowOverCap=true, maxMinutes=urgent_handle)
if spikeDetected() OR overdue(service): escalateAddPeople(service)
return a
function dispatchBranchProject(service):
for s in uniqueSkillSteps(service):
pinAssign(soleOwner(s.skill), s) // ràng buộc cứng
crew = []
while not covers(crew.skills, remainingSkills(service)):
crew.add(roundRobinNext(eligiblePool(service.area)))
return assignCrew(crew, service)
Ghi chú thiết kế
Band là tiêu chí ưu tiên (cộng điểm w4), không phải bộ lọc — người vượt band vẫn nằm trong cands, chỉ bị điểm w4 thấp hơn. Van an toàn ở nhánh cands rỗng đảm bảo không bao giờ pending.
Gợi ý API / sự kiện
| Endpoint / Event | Mô tả |
POST /services | Tiếp nhận việc mới → trigger dispatch() |
POST /steps/{id}/complete | NV cập nhật step xong → tính lại tải, mở step kế |
GET /queue?area=&skill= | Hàng đợi việc pending cho NV xem & tự nhận |
POST /assignments/{id}/self-pick | NV chủ động nhận thêm |
POST /assignments/{id}/return | Trả phần cụm còn lại vào hàng đợi |
EVENT urgent.created | Kích hoạt dispatchUrgent |
EVENT skill.unique.detected | Cảnh báo HR đào tạo dự phòng |
Edge case
| Tình huống | Xử lý mong đợi |
| Không ai trong band rảnh | Giao cho bất kỳ ai đủ KN rảnh, gắn cờ out_of_band |
| Không ai có kỹ năng của step | Đẩy cảnh báo điều phối thủ công; không tự gán sai người |
| NV làm xong sớm, còn việc tồn | Cho self-pick kể cả ngoài band |
| Khẩn khi NV đang giữa cụm | Giao thêm, không cắt; NV được quyền trả phần còn lại |
| Step cần 2 người | Gán 2 người đủ KN, chia lương theo split_ratio |
| Kỹ năng độc nhất, người đó nghỉ | Dự án đứng; alert HR; không có backup hợp lệ |
| Cụm vượt 4h | Cắt tại step làm tổng ≤ trần; phần dư về hàng đợi |
| Nhiều người cùng được gán đồng thời | Cần transaction/lock (optimistic locking hoặc last-write-wins) |
Bản đặc tả đầy đủ kèm pseudocode chi tiết có trong tài liệu Word/PDF.
PDF
Word