-
Phạm vi: Flows “Khách đặt lịch → Thợ xử lý → Thanh toán → Ví thợ → Rút tiền”, kèm flow đặt lịch qua AI và các flow phụ trợ (GPS, Review/Rating, Dispute, Voucher, Escrow, Payout).
-
Không phạm vi (ngoài bản này): CMS nội dung marketing, quản trị người dùng cấp hệ thống phức tạp, kế toán thuế chi tiết, tích hợp KYC nâng cao (chỉ nêu pre-check cơ bản), tính năng cộng tác nội bộ nâng cao.
-
Mục tiêu:
- Diễn giải chi tiết từng bước cho Mobile/Web.
- Ràng buộc trạng thái bám enums đã khai báo.
- Xác định rules (giá/hoa hồng/escrow/voucher) và edge cases.
- Chuẩn hoá thông báo (push/in-app), logs, AC để QA test.
- Customer (Khách hàng): tạo yêu cầu, nhận báo giá, xác nhận, thanh toán, đánh giá.
- Technician (Thợ): nhận đơn, báo giá, di chuyển/kiểm tra, sửa chữa, hoàn tất, rút tiền.
- Admin/Operator: xử lý payout, xử lý tranh chấp (Dispute), kiểm soát gian lận, cấu hình phí/hoa hồng, phê duyệt ngoại lệ.
- AI Service: gợi ý khoảng giá, chẩn đoán ban đầu, kiểm tra sai số báo giá.
- Đơn vị tiền tệ: VND (số nguyên).
- Múi giờ: Asia/Ho_Chi_Minh.
- Khoảng giá AI:
minPrice,maxPrice; ngưỡng vượt X% cấu hình theoServiceCategory. - Commission: cấu hình theo
ServicehoặcServiceCategoryhoặc toàn hệ thống (ưu tiên theo mức cụ thể nhất). - Escrow: chỉ áp dụng Online; Cash không escrow (ghi nợ hoa hồng nếu có).
- Hình ảnh/video: yêu cầu ở mốc trạng thái quan trọng (Before/After/Exceed/Payment).
- GPS: ghi
SystemLogkhi thợ cập nhật EN_ROUTE/ARRIVED. - Voucher: áp dụng theo ràng buộc
VoucherCategory/VoucherService/VoucherPaymentMethodvà thời gian hiệu lực.
- Job =
ServiceRequest+ServiceDeliveryOffer+ServiceAppointment+Payment(+AI_Diagnosis,Media,Review,ActivityLog). - Quote:
ServiceDeliveryOffer(estimatedCost/finalCost) ↔ trạng thái PENDING/ACCEPTED/REJECTED/EXPIRED/CHECKING_AWAIT. - Appointment: lịch làm việc của thợ sau khi quote được ACCEPTED.
- Payment: theo
PaymentMethod(Online/Cash), bám các trạng thái PENDING → PAYMENT_SUCCESS → (ESCROW) → COMPLETE | FAILED. - Wallet:
WalletAccount(per Technician) +WalletTransaction(Credit/Debit; reference tớiPayment/Appointment/... ). - Payout:
WalletPayoutRequest(yêu cầu rút tiền) +WalletTransaction(Debit/Withdrawal).
States: PENDING, QUOTED, QUOTE_ACCEPTED, QUOTE_REJECTED, COMPLETED, CANCELLED
Default: PENDING
Allowed transitions
- PENDING → QUOTED — khi có ≥ 1 offer hợp lệ.
- QUOTED → QUOTE_ACCEPTED — khi khách chấp nhận 1 offer.
- QUOTED → QUOTE_REJECTED — khi khách từ chối offer (và không còn offer khác).
- QUOTE_ACCEPTED → COMPLETED — khi Appointment=REPAIRED và Payment=COMPLETE.
- QUOTE_ACCEPTED → CANCELLED — theo policy huỷ.
- QUOTE_REJECTED → CANCELLED — khi tất cả offer bị từ chối/hết hạn.
Guards & notes
QUOTEDnghĩa là request đã có ít nhất một offer hợp lệ.COMPLETEDchỉ khi job đã REPAIRED và thanh toán COMPLETE.
States: PENDING, ACCEPTED, REJECTED, EXPIRED, CHECKING_AWAIT
Default: PENDING
Allowed transitions
- PENDING → ACCEPTED — khi customer chấp nhận báo giá (tạo Appointment).
- PENDING → REJECTED — khi customer từ chối báo giá.
- PENDING → EXPIRED — offer quá hạn
offerTtl. - PENDING → CHECKING_AWAIT — thợ cần kiểm tra tại chỗ/giá vượt ngưỡng AI.
- CHECKING_AWAIT → ACCEPTED — khi khách xác nhận đặc biệt.
Terminal: ACCEPTED, REJECTED, EXPIRED.
States: SCHEDULED, EN_ROUTE, ARRIVED, CHECKING, REPAIRING, REPAIRED, CANCELLED, DISPUTE, ABSENT
Default: SCHEDULED
Nominal path
- SCHEDULED → EN_ROUTE → ARRIVED → CHECKING → REPAIRING → REPAIRED
Other transitions
- CHECKING → CANCELLED — nếu khách từ chối sau kiểm tra.
- Any of (SCHEDULED..REPAIRING) → ABSENT — nếu không gặp được khách.
- Any of (SCHEDULED..REPAIRED) → DISPUTE — khi mở tranh chấp.
Guards & notes
- Cập nhật EN_ROUTE/ARRIVED phải ghi
SystemLog(GPS). - Không cho “nhảy cóc” trạng thái trong nominal path.
States: PENDING, PAYMENT_SUCCESS, ESCROW, COMPLETE, FAILED
Online (ưu tiên)
- PENDING → PAYMENT_SUCCESS → ESCROW → COMPLETE
- PENDING → FAILED
Tiền mặt (CASH)
- PAYMENT_SUCCESS → COMPLETE (gần như đồng thời)
Guards & notes
- Online chỉ cộng Ví thợ khi COMPLETE (sau escrow).
- Cash tạo ghi nợ hoa hồng nếu có:
WalletTransaction(Debit, Commission).
States: OPEN, IN_REVIEW, RESOLVED
Allowed transitions
- OPEN → IN_REVIEW
- IN_REVIEW → RESOLVED (refund/partial/adjust commission)
Notes
- Có thể mở từ SCHEDULED đến sau REPAIRED (trước khi đóng job).
Steps
- App hiển thị màn chào.
- Hỏi vai trò Thợ/Khách (có nhớ lựa chọn).
- Điều hướng tới trang chủ tương ứng.
Rules
- Lưu lựa chọn vai trò vào profile hoặc local (nếu đã xác thực).
- Nếu user là cả hai (được cấp quyền), cho phép switch role trong Profile.
AC
- Given user mới mở app, When chọn “Thợ”, Then vào Home thợ và không hỏi lại ở lần sau nếu bật “Nhớ”.
Steps
- Vào Home khách → chọn Dịch vụ → Đặt lịch.
- Nhập: họ tên, số ĐT, địa chỉ (chọn Address đã lưu hoặc nhập mới), thời gian (ngay/hẹn), mô tả + ảnh/video.
- Submit → tạo
ServiceRequest(PENDING)+Media(INITIAL)nếu có.
Validation
- Địa chỉ: city/province/lat/lng hợp lệ.
- Thời gian: không < now, cách now ≥ minLeadTime.
- Tối thiểu 1 mô tả (text hoặc media).
- Rate limit tạo yêu cầu (anti-spam).
AC
- Given form hợp lệ, When submit, Then tạo
ServiceRequest(PENDING) và push thông báo tới thợ phù hợp.
Steps
- Home Thợ → Hàng chờ lọc theo
Service/Skill/Location. - Mở chi tiết request (ảnh/video, mô tả, nếu có AI_Diagnosis).
- Nhập báo giá: Giá chốt hoặc Giá dự kiến
estimatedCost. - Nếu vượt ngưỡng AI (X% ngoài khoảng), buộc nhập lý do + media minh chứng →
Media(EXCEED). - Tạo
ServiceDeliveryOffer(PENDING); hệ thống setServiceRequest=QUOTEDnếu là offer đầu tiên.
Rules
- Một thợ có thể tạo 1 offer/1 request (tránh spam).
- Offer hết hạn sau
offerTtl(→ EXPIRED). - AI gợi ý khoảng giá hiển thị cho cả thợ & khách.
AC
- Given thợ nhập giá nằm trong khoảng AI, When submit, Then offer=PENDING và request=QUOTED.
Steps
- Khách nhận push: “Có báo giá từ thợ X”.
- Màn hình hiển thị giá + khoảng AI + badge cảnh báo nếu vượt ngưỡng.
- Khách chọn Chấp nhận hoặc Từ chối.
Rules
- Khi Chấp nhận:
ServiceDeliveryOffer=ACCEPTED,ServiceRequest=QUOTE_ACCEPTED, tạoServiceAppointment(SCHEDULED)vớischeduledDate(theo thời gian khách chọn). - Khi Từ chối:
ServiceDeliveryOffer=REJECTED. Nếu tất cả offer bị từ chối →ServiceRequest=CANCELLED(hoặc vẫn QUOTED nếu còn offer khác/đang chờ).
AC
- Given khách bấm Chấp nhận, When confirm, Then auto tạo Appointment và cố định thợ.
Steps
- Thợ cập nhật: EN_ROUTE → ARRIVED (ghi
SystemLog+ GPS). - Gửi ảnh/video ban đầu
Media(INITIAL)nếu thiếu. - Nếu trước đó là giá dự kiến: vào CHECKING, nhập finalCost.
- Nếu finalCost vượt ngưỡng AI/estimatedCost theo rule →
ServiceDeliveryOffer=CHECKING_AWAITvà yêu cầu khách xác nhận đặc biệt.
Rules
- Trạng thái Appointment phải theo chuỗi hợp lệ (không nhảy cóc).
- Mọi cập nhật giá cần log vào
ActivityLog(type=UPDATED, field=cost).
AC
- Given thợ đặt ARRIVED, When app gửi, Then hiển thị mốc đến nơi cho khách theo thời gian thực (GPS tracking).
Steps
- Khách xem finalCost + cảnh báo AI (nếu có).
- Chọn: Chấp nhận hoặc Từ chối (SOS).
Rules
- Nếu Chấp nhận: Appointment chuyển REPAIRING.
- Nếu Từ chối (SOS): mở luồng DISPUTE hoặc CANCELLED theo lý do.
AC
- Given finalCost thay đổi, When khách chấp nhận, Then cho phép thợ bắt đầu REPAIRING.
Steps
- Thợ cập nhật REPAIRING.
- Sau khi xong: REPAIRED, gửi ảnh/video sau sửa
Media(FINAL)+ ghi chú. - Chuyển sang Thanh toán.
AC
- Given thợ set REPAIRED và upload Media(FINAL), When lưu, Then cho phép mở màn thanh toán.
Steps
- Khách chọn phương thức (MOMO/ZALOPAY/VNPAY/BANK_QR/CREDIT_CARD/BANK_TRANSFER).
- Tạo
Payment(PENDING)và gọi cổng. - Thành công:
Payment=PAYMENT_SUCCESS, setPayment=ESCROW. - Khi khách xác nhận hoàn tất hoặc hệ thống auto-complete sau
T+N:Payment=COMPLETE→ ghi thu nhập ròng cho thợ.
Hạch toán Ví thợ
NetEarning = Gross - Commission.- Ghi
WalletTransaction(Credit, Reason='Earning', reference=Payment); cập nhậtWalletAccount.Balance += NetEarning.
AC
- Given payment success, When Appointment=REPAIRED & customer confirm, Then Payment=COMPLETE và Ví thợ +NetEarning.
Steps
- Thợ thu tiền trực tiếp, chụp ảnh biên nhận
Media(PAYMENT). - Tạo
Payment(PAYMENT_SUCCESS → COMPLETE)vớimethod=CASH(không escrow). - Hệ thống ghi hoa hồng phải thu:
WalletTransaction(Debit, Reason='Commission');WalletAccount.Balance -= Commission. - Nếu Balance < 0: hiển thị nợ hoa hồng (bù khi có thu nhập mới hoặc lúc rút tiền).
AC
- Given method=CASH, When mark paid, Then ghi nợ hoa hồng (nếu có) và hiển thị công nợ trên Ví thợ.
Steps
- Payment COMPLETE → thông báo cho cả hai.
- Khách đánh giá
Review(ratingOverall, reviewDate).
Rules
- Sau COMPLETE:
ServiceRequest=COMPLETED(nếu không ở trạng thái DISPUTE). - Review chỉ mở trong
reviewWindowngày.
AC
- Given Payment=COMPLETE, When success, Then request chuyển COMPLETED và mở form Review.
Steps
- Thợ mở Ví của tôi → xem số dư khả dụng + lịch sử.
- Tạo
WalletPayoutRequest(amount ≤ balance, payoutChannel). - Admin xử lý: duyệt/chuyển khoản.
- Thành công: ghi
WalletTransaction(Debit, Reason='Withdrawal');WalletAccount.Balance -= amount.
Gợi ý Status (nên bổ sung enum)
WalletPayoutRequest.status: PENDING → APPROVED → PAID | REJECTED | FAILED.
AC
- Given số dư đủ, When tạo payout, Then trạng thái = PENDING và khoá số dư tương ứng (nếu bật cơ chế hold).
- Khoảng AI hiển thị cho cả hai phía.
- Ngưỡng vượt X% phải: (a) nhập lý do, (b) đính kèm media minh chứng, (c) hiển thị cảnh báo cho khách, (d) cần xác nhận đặc biệt từ khách.
- Ghi nhận vào
AI_Diagnosis(predictedIssue,confidenceScore,isLatest).
- Từ
estimatedCost→finalCost: nếu thay đổi vượtdeltaLimithoặc ngoài khoảng AI → chuyểnServiceDeliveryOffer=CHECKING_AWAITvà chặn tiến trình cho tới khi khách xác nhận.
-
Áp dụng khi thanh toán Online trừ khi
VoucherPaymentMethodcho phép phương thức khác. -
Kiểm tra: thời gian hiệu lực,
isActive, ràng buộc theoServiceCategory/Service. -
Voucher.discountType:PERCENTAGE: giảm theo %.FIXED_AMOUNT: giảm số tiền cố định.FREE_CHECKING: miễn phí bước kiểm tra (áp dụng vào công kiểm tra).
-
Ghi
VoucherUsage(link tớiserviceRequestID&paymentID).
Commission = rule(service/category/global); có thể có minCommission.- Tính trên Gross trước voucher hay sau? → Quy ước: tính trên giá sau voucher (có thể cấu hình).
- Log chi tiết vào
ActivityLogkhi rule thay đổi.
WalletAccount.Balance = Σ(Credits) - Σ(Debits)(không tính các khoản hold nếu chưa trừ).- Không cho rút > balance khả dụng (balance - holds).
- Cho phép âm sau Cash Commission → hiển thị công nợ.
- Trước EN_ROUTE: khách huỷ miễn phí.
- Sau EN_ROUTE, trước ARRIVED: phí huỷ nhỏ (có thể cấu hình).
- Sau ARRIVED: tính phí kiểm tra (nếu có).
- Thợ huỷ nhiều lần → ảnh hưởng rating/điểm kỷ luật.
- Mở
Dispute(OPEN)với lý do + media. - Admin chuyển
IN_REVIEW, yêu cầu thêm chứng cứ. - Kết quả
RESOLVED: refund/tách phần việc/điều chỉnh commission.
- Mọi thay đổi quan trọng ghi
ActivityLog(CREATED/UPDATED/APPROVED/CANCELLED/ASSIGNED/COMPLETED/DISPUTED). SystemLogdùng cho check-in GPS & hành vi thực địa (arrival, movement).
Steps
- Khách vào Chẩn đoán lỗi → chụp/quay + mô tả.
- AI phân tích → trả về chẩn đoán, bước sơ cứu, khoảng giá.
- Khách bấm Đặt lịch từ màn hình AI → tạo
ServiceRequestkèmAI_Diagnosis&Media.
Rules
- Kết quả AI không thay thế kiểm tra của thợ, chỉ định hướng.
- Báo giá thợ vẫn phải bám khoảng AI (nếu vượt → quy tắc vượt ngưỡng).
AC
- Given AI trả về kết quả, When khách đặt lịch từ đó, Then form auto điền dữ liệu AI và tạo request PENDING.
| Sự kiện | Người nhận | Nội dung chính | Kênh |
|---|---|---|---|
| Request CREATED | Thợ phù hợp | Yêu cầu mới gần bạn | Push + In-app |
| Offer RECEIVED | Khách | Thợ X đã báo giá | Push + In-app |
| Offer EXPIRED | Thợ | Báo giá hết hạn | In-app |
| Offer ACCEPTED | Thợ | Khách đã chấp nhận, lịch SCHEDULED | Push + In-app |
| EN_ROUTE | Khách | Thợ đang di chuyển | In-app + Map |
| ARRIVED | Khách | Thợ đã đến | In-app |
| FinalCost UPDATE | Khách | Giá cuối cần xác nhận | Push + In-app |
| Appointment REPAIRED | Khách | Sẵn sàng thanh toán | In-app |
| Payment SUCCESS/ESCROW | Thợ | Đã thanh toán (escrow) | In-app |
| Payment COMPLETE | Cả hai | Đơn hoàn tất | Push + In-app |
| Payout PAID | Thợ | Rút tiền thành công | Push + In-app |
| Dispute OPEN/RESOLVED | Cả hai | Trạng thái tranh chấp | In-app |
- Hàng chờ thợ: empty ↔ gợi ý bật định vị / mở filter.
- Chi tiết request: lỗi media → retry upload; hiển thị badge “AI suggested”.
- Thanh toán online: timeout/FAILED → hiển thị retry & đổi phương thức.
- Ví thợ: nếu âm → hiển thị “công nợ hoa hồng”.
- Payout: đang PENDING → banner nhắc thời gian xử lý.
| Tác vụ | Customer | Technician | Admin |
|---|---|---|---|
| Tạo/huỷ ServiceRequest | ✔️ | ❌ | ✔️ |
| Tạo Offer | ❌ | ✔️ | ❌ |
| Nhận/Chuyển trạng thái Appointment | ❌ | ✔️ | ✔️ |
| Cập nhật giá (finalCost) | ❌ | ✔️ | ✔️ |
| Thanh toán | ✔️ | ❌ | ✔️ (refund/adjust) |
| Tạo Dispute | ✔️ | ✔️ | ✔️ |
| Duyệt Payout | ❌ | ❌ | ✔️ |
-
ServiceRequest.status:
PENDING → QUOTED → (QUOTE_ACCEPTED|QUOTE_REJECTED) → (COMPLETED|CANCELLED)- Note:
COMPLETEDchỉ khiPayment=COMPLETEvàAppointment=REPAIRED.
- Note:
-
ServiceDeliveryOffer.status: dùng
CHECKING_AWAITkhi chờ xác nhận finalCost vượt ngưỡng. -
ServiceAppointment.status: dùng đúng chuỗi, ABSENT khi không gặp được khách.
-
Payment.status: Online =
PENDING → PAYMENT_SUCCESS → ESCROW → COMPLETE; Cash =PAYMENT_SUCCESS → COMPLETE. -
Media.mediaType:
INITIAL/FINAL/EXCEED/PAYMENT/OTHERkhớp các mốc upload. -
ActivityLog.action: ghi đủ CREATED/UPDATED/APPROVED/CANCELLED/ASSIGNED/COMPLETED/DISPUTED.
Khoảng trống đề xuất bổ sung
WalletTransaction.type&reason(EARNING, COMMISSION, WITHDRAWAL, ADJUSTMENT).WalletPayoutRequest.status: PENDING/APPROVED/PAID/REJECTED/FAILED.Dispute.reason(enum mô tả: overcharge, poor quality, no show...).
- Funnel: PENDING → QUOTED → ACCEPTED → ARRIVED → REPAIRED → COMPLETE.
- Tỉ lệ vượt ngưỡng AI, tỉ lệ bị từ chối, tỉ lệ huỷ theo mốc.
- ARPU thợ, Commission/day, Voucher burn rate.
- Thời gian EN_ROUTE, tỉ lệ ABSENT.
- Ẩn PII khỏi logs; mã hoá token cổng thanh toán.
- Chống spam/giả mạo ảnh (hash, EXIF basic).
- Chống định vị giả (basic checks, server-side distance validation).
- Role-based access theo bảng Permission.
- Ngưỡng X% theo ServiceCategory hay Service?
- Commission tính trước/ sau voucher (hiện quy ước “sau”, cần flag cấu hình).
offerTtl,reviewWindow,autoCompleteDaysgiá trị mặc định?- Có hold số dư khi tạo
WalletPayoutRequest? (đề xuất có) - Kịch bản refund online (1 phần/ toàn phần) và điều chỉnh
WalletTransaction.