Audit Yêu Cầu Cập Nhật Tính Năng App
Cập nhật: 2026-05-15, rà lại toàn diện ngoài provider messaging và BigFan
Phạm vi: mobileApp và goldenlotus-backend/docker/vendure_backend/apps/server/src/plugins/golden-lotus
Ngoài Phạm Vi Lần Này
Theo yêu cầu rà lại lần này, không audit/không tính ưu tiên cho:
- Provider messaging: WhatsApp, Messenger, Zalo/Firebase/Expo adapter.
- BigFan: data contract, CMS/API riêng, voucher/quà sinh nhật theo ngày.
Kết Luận Nhanh
Không tính provider messaging và BigFan, app hiện đạt khá nhiều yêu cầu ở mức UI + API/backend guard. Các phần đã tương đối chắc gồm: đa ngôn ngữ app Nga/Nhật, logic giới hạn ngày/khách booking, dropdown chọn số lượng, hotline khi vượt giới hạn, auto-fill profile, note booking, countdown booking live refresh, event slider/detail, News search/load-more/detail qua API, GPS prompt, profile địa lý và delete account.
Các gap thực tế còn lại:
- CMS/admin typed cho booking policy vẫn thiếu:
maxAdvanceDays,maxPartySize,reminderMinutesBefore,lateAutoCancelEnabled,lateGraceMinuteshiện có backend config nhưng chưa có màn quản trị rõ ràng. - QR thành viên đã có màn riêng ở app; nếu cần POS/staff scan xác thực thì vẫn nên bổ sung signed payload API riêng.
- Location prompt đã có trong app và đã refetch home content theo
branchIdkhớp region khi xác định được vị trí; backend vẫn chưa nhận tọa độ/region thô để tự quyết định khu vực.
Đã xử lý thêm trong lượt này:
NewsScreenchuyển sanglotusArticles(input:{languageCode, search, skip, take})để search/load-more server-side; thêm màn detail/article/[slug]dùnglotusArticle(slug, languageCode).HomeBeautySectionvà News list mở bài viết vào màn article detail, không còn đi nhầm route/offers/[id].ScheduleScreenrefresh countdown mỗi 60 giây khi người dùng đang đứng trên màn.HomeScreendùng region từ GPS để tìm branch phù hợp và refetchlotusMobileHome(input:{ branchId, languageCode })khi có branch match.- Nút xem tất cả lịch sử điểm mở màn
/points/history, gọilotusMyPointLedger(input:{skip,take})với load-more. - Icon QR trên Home mở
/member/qr, hiển thị tên, hạng thành viên ngay trên QR; QR voucher vẫn giữ route riêng/offers/qr.
Đã xử lý thêm luồng Auth/OTP theo feedback khách hàng:
- Đăng ký không còn cho email đã là thành viên đi tiếp để nhận OTP; backend trả lỗi rõ và mobile gợi ý đăng nhập/quên PIN.
- Login bằng mã PIN đã nhận cả email hoặc số điện thoại; backend resolve email/phone đã verified trước khi check PIN.
- OTP đăng nhập và OTP quên PIN đã tự chọn kênh theo identifier: email gửi qua Brevo SMTP, số điện thoại gửi qua eSMS.
- Mobile chỉ gửi
identifierlên OTP/PIN API; backend tự suy ra email hay số điện thoại, không hard-code kênh gửi ở app. - Mobile Login có thêm nhánh "Đăng nhập bằng OTP", resend cooldown 60 giây, verify OTP xong vào app như login PIN.
.env.exampleđã có placeholder Brevo/eSMS;.envruntime đang dùng sender Brevo domainazns.vnvà eSMS ở sandbox để test SMS an toàn.
Tổng Hợp Trạng Thái Ngoài Scope Bị Loại
| Nhóm yêu cầu | Trạng thái hiện tại | Mức hoàn thiện | Việc còn lại |
|---|---|---|---|
| Đa ngôn ngữ app Nga/Nhật | Có ru/ja translation bundle, language picker, persist/restore, sync profile preference, query content truyền languageCode. | Đã có ở mức app | Native QA/copy review trước release; CMS content đa ngôn ngữ để làm sau. |
| Booking policy ngày/khách | App đọc policy, UI/validation theo policy, backend reject vượt ngày/khách. | Đã có core end-to-end | Bổ sung CMS UI typed + test boundary timezone/party size. |
| Booking form UX | Dropdown guest, hotline over-limit, auto-fill phone/email, note gửi backend. | Đã có end-to-end | Chuẩn hóa hotline bắt buộc/fallback; test dữ liệu branch thiếu phone. |
| Reminder/auto no-show | Backend có scheduled task, reminder window, no-show theo lateAutoCancelEnabled. | Partial | Cần CMS UI typed và chốt automation enabled có tắt reminder hay chỉ no-show. |
| Membership point UI | Đã rút gọn progress, nổi bật điểm sắp hết hạn, có lịch sử ngắn và màn lịch sử đầy đủ. | Đã có app-side | Expiry mới summary bucket; nếu cần nhiều đợt hết hạn phải mở rộng API. |
| QR thành viên | Có màn QR thành viên riêng /member/qr, rank nằm ngay phía trên QR. | Đã có UI app | Payload hiện tạo local từ member id/rank; nếu quầy/POS cần scan xác thực thì thêm signed payload API riêng. |
| Location prompt | App hỏi quyền GPS, lưu region, prioritize local và refetch home content bằng branchId nếu tìm được branch cùng region. | Partial+ | Backend chưa có input region/latitude/longitude; personalization hiện là branch-based, không phải geo-query thật. |
| Event UI | Dotted slider, tab chi nhánh client-side, detail không còn CTA đăng ký. | Đã có UI | Branch filter server-side và route naming event/offer nên tách rõ hơn. |
| News UI | Search bar/load more/detail dùng lotusArticles/lotusArticle với languageCode. | Đã có app-side | Test thực tế dữ liệu CMS đa ngôn ngữ và phân trang lớn. |
| Profile/settings | Province/ward picker và delete account đã có end-to-end. | Đã có | Chốt taxonomy label Quận/Huyện vs Province/Ward; tăng xác nhận delete account nếu cần store policy. |
1. Module Đặt Lịch (Booking)
| Yêu cầu | Trạng thái | Bằng chứng | Gap / việc cần làm |
|---|---|---|---|
| Đa ngôn ngữ Nga/Nhật | Đã có ở mức app | locales/i18n.ts load vi/en/ru/ja; ru.json và ja.json đã dịch đủ key/placeholder; locales/language.ts định nghĩa language picker + persist; SettingsScreen có Nga/Nhật; _layout.tsx restore language; LanguagePreferenceGate sync lotusMyAppPreferences.languageCode; home/offers/services query truyền languageCode. | Nên native QA/copy review bản Nga/Nhật trước release. CMS quản trị nội dung theo ngôn ngữ để làm sau như đã chốt. |
| Chỉ đặt trước tối đa 30 ngày, Admin chỉnh được | Partial | Mobile đọc bookingPolicy.maxAdvanceDays; backend default 30 và validate trong createMyBooking. | Admin chỉnh qua provider config JSON, chưa có UI CMS chuyên biệt/field typed. Cần màn cấu hình booking policy rõ ràng. |
| Giới hạn 5 người/booking, Admin chỉnh được | Partial | Mobile dropdown theo maxPartySize; backend default 5 và reject vượt limit. | Tương tự: cần UI CMS chuyên biệt. |
| UX chọn số lượng dạng Select/Dropdown | Đã có | LocationScreen dùng picker option list, không phải text input. | Có thêm option > max mở hotline, đúng hướng. |
| Nếu > giới hạn hiển thị hotline chi nhánh | Đã có UI | showHotlineMessage() lấy selectedBranch.phone và mở tel:. | Cần đảm bảo mọi branch CMS có hotline/phone hợp lệ. |
| Auto-fill phone/email từ Profile | Đã có UI | applyProfileContactPrefill() fill fullName, phone, email nếu user chưa sửa. | Phụ thuộc auth profile đã được restore đúng. |
| Thêm trường Note | Đã có end-to-end | Mobile có draft.note; mutation gửi note; backend LotusBooking.note. | Không thấy gap chính. |
| Countdown trong quản lý lịch đặt | Đã có UI live refresh | ScheduleScreen.formatCountdown() hiển thị còn ngày/giờ/phút và state nowMs refresh mỗi 60 giây. | Không thấy gap chính; nên QA khi app background/foreground. |
| Push reminder trước giờ hẹn ít nhất 15 phút | Partial | Backend default reminderMinutesBefore: 15, scheduled task mỗi 5 phút, push qua LotusExpoPushService. | Scheduled automation hiện chưa check setting.enabled trước khi chạy reminder; cần xác nhận design bật/tắt. Cần push credentials/device registration production. |
| Auto-cancel/no-show khi trễ quá số phút, Admin bật/tắt | Partial | Backend policy có lateAutoCancelEnabled, lateGraceMinutes; markLateBookingsNoShow() chuyển NO_SHOW. | Chưa có CMS UI chuyên biệt. Logic đặt tên là NO_SHOW chứ không phải CANCELLED; cần chốt nghiệp vụ có muốn hủy thật hay no-show. |
Audit Chi Tiết Logic Chọn Số Lượng
| Hạng mục | Trạng thái | Bằng chứng kỹ thuật | Nhận xét / gap |
|---|---|---|---|
| Chỉ đặt lịch trước tối đa 30 ngày tới | Đã có end-to-end | Mobile default defaultBookingPolicy.maxAdvanceDays = 30; app đọc policy từ lotusMobileBootstrap.bookingPolicy; TimeScreen.buildDays(maxAdvanceDays) chỉ render số ngày theo policy; validateBookingDraft() chặn thời gian vượt policy.maxAdvanceDays; backend assertBookingPolicy() cũng chặn startsAt > now + maxAdvanceDays. | Đã có guard cả UI và backend. Cần test thực tế boundary ngày thứ 30 với timezone/giờ cuối ngày để tránh lệch kỳ vọng nghiệp vụ. |
| Admin điều chỉnh số ngày đặt trước | Partial | Backend LotusProviderConfigService.normalizeBookingPolicy() đọc maxAdvanceDays từ provider config và clamp 1-365; mobile tự nhận qua bootstrap. | Có contract/config backend nhưng chưa thấy CMS UI typed/chuyên biệt cho admin chỉnh; hiện vẫn là gap CMS. |
| Giới hạn tối đa 5 người/booking | Đã có end-to-end | Mobile default defaultBookingPolicy.maxPartySize = 5; LocationScreen sinh guestOptions từ bookingPolicy.maxPartySize; validateBookingDraft() chặn guests > policy.maxPartySize; backend assertBookingPolicy() reject partySize > policy.maxPartySize. | Đã chặn client và server. Nên bổ sung test regression cho partySize = max, partySize = max + 1. |
| Admin điều chỉnh giới hạn khách trên CMS | Partial | Backend đọc maxPartySize từ provider config và clamp 1-50; mobile dropdown tự đổi theo giá trị policy trả về. | Logic app/API đã sẵn sàng, nhưng thiếu UI CMS typed để admin tự chỉnh số này rõ ràng. |
| UX chọn số lượng là Select/Dropdown, không nhập tay | Đã có UI | LocationScreen dùng TouchableOpacity mở optionList; các option được generate từ 1..maxPartySize; không dùng TextInput cho guests. | Đúng yêu cầu. Nếu maxPartySize lớn, dropdown có thể dài; backend đang cho tối đa 50 nên nên cân nhắc bottom sheet/scroll picker nếu admin tăng cao. |
| Nếu khách muốn đặt hơn giới hạn thì hiện thông báo hotline chi nhánh | Đã có UI | LocationScreen thêm option moreThanGuests; chọn option gọi showHotlineMessage(), hiển thị Alert với selectedBranch.phone; nếu có phone thì có nút Call hotline mở tel:. | Phụ thuộc dữ liệu branch có phone. Nếu branch thiếu phone, app vẫn báo cần liên hệ hotline nhưng không có nút gọi; cần bắt buộc hotline trong CMS hoặc thêm hotline fallback toàn hệ thống. |
2. UI/UX Membership
| Yêu cầu | Trạng thái | Bằng chứng | Gap / việc cần làm |
|---|---|---|---|
| QR thành viên hiển thị Rank phía trên QR | Đã có UI app | MemberQrScreen route /member/qr hiển thị name, rankLabel, rankValue ngay trước QR; Home QR icon trỏ sang route này. | Cần signed payload API/POS scan nếu nghiệp vụ cần xác thực QR thật tại quầy. |
| Rút gọn chặng xét hạng, xóa biểu đồ tròn | Đã có theo app hiện tại | PointScreen không còn biểu đồ tròn; dùng rank card + expiring card + history. | PointSummary vẫn còn các field route* legacy trong type/API mapping; có thể dọn sau. |
| Làm nổi bật điểm sắp hết hạn + ngày kết thúc | Đã có UI/API | PointScreen có expireCard; backend lotusMyPointSummary trả expiringSoonPoints, nextExpiryAt. | Nếu cần nhiều đợt hết hạn, API hiện gom thành 1 summary/next expiry, chưa list nhiều bucket. |
| Lịch sử dùng điểm dễ nhận thấy hơn | Đã có UI + full list | historyCard có header/icon/list; nút "see all" điều hướng /points/history; màn full list dùng lotusMyPointLedger(input:{skip,take}) và load-more. | Không thấy gap chính; nên QA data lớn. |
| Popup xin quyền GPS khi mở app | Đã có UI + branch-based query | HomeScreen show Alert một lần khi locationPromptEnabled, gọi Expo Location, lưu region, sort local và refetch lotusMobileHome bằng branchId match region. | Backend chưa nhận region/latitude/longitude; nếu cần đúng bán kính GPS thì phải thêm contract geo-query. |
3. Sự Kiện (Event) & Tin Tức (News)
| Yêu cầu | Trạng thái | Bằng chứng | Gap / việc cần làm |
|---|---|---|---|
| Event dotted slider | Đã có UI | HomeEventSection horizontal scroll + dots. | Cần kiểm tra pixel/gesture thật trên thiết bị. |
| Bỏ nút Đăng ký ở chi tiết Event | Đã có UI | OfferDetailScreen chỉ render bottom CTA khi offer.kind === 'voucher'; event không có nút đăng ký. | Backend vẫn còn registerLotusEvent; nếu business bỏ hẳn đăng ký, cần ẩn/remove flow entry khác. |
| Nhấn banner để xem chi tiết | Đã có UI | Event card push sang /offers/[id] detail. | Route dùng chung offers/events, naming hơi gây nhầm. |
| Tab filter event theo chi nhánh | Đã có UI | HomeEventSection tạo branch tabs từ event.location. | Backend có branchId/branchCode filter nhưng app home đang filter client-side theo text location, chưa gọi server theo branch. |
| News detail/category nhiều bài hơn, load more/pagination | Đã có app-side | NewsScreen gọi lotusArticles(input:{ languageCode, search, skip, take }); nút load more tăng take; có màn /article/[slug] gọi lotusArticle(slug, languageCode). | Cần QA dữ liệu CMS đa ngôn ngữ và trường hợp slug thiếu bản dịch theo ngôn ngữ. |
| Search bar News | Đã có app-side | Search debounced 300ms và truyền vào lotusArticles.input.search. | Backend search đang filter trong memory sau khi lấy active rows; nếu dữ liệu lớn nên tối ưu DB query sau. |
4. Profile & Settings
| Yêu cầu | Trạng thái | Bằng chứng | Gap / việc cần làm |
|---|---|---|---|
| Thêm Quận/Huyện, Khu phố/Phường chọn sẵn | Đã có end-to-end | EditProfileScreen chọn province/ward; backend có lotusVietnamProvinces, lotusVietnamWards, updateLotusMyProfile. | Label hiện gọi district nhưng dữ liệu là province/city + ward theo bảng 2026; cần chốt taxonomy theo yêu cầu mới. |
| Delete Account dưới About/Giới thiệu | Đã có end-to-end | SettingsScreen đặt delete row sau Terms/Privacy; backend deleteLotusMyAccount soft delete customer. | Nên thêm xác nhận mạnh hơn, lý do xóa, và copy App Store/Google Play nếu cần. |
Rủi Ro Kỹ Thuật Nên Chốt Sớm
useMockmặc định đang làtruetrong mobile env, nên nhiều màn vẫn có thể chạy với dữ liệu mock dù API đã có.- Gói
ru/jađã dịch đủ cấu trúc app, nhưng vẫn nên có native QA/copy review trước khi release cho khách Nga/Nhật. - Booking automation scheduled task được đăng ký ở plugin config; phần reminder không kiểm tra
LotusAutomationSetting.enabled, trong khi no-show lại kiểm theo booking policy. Cần chốt "tắt automation" nghĩa là tắt tất cả hay chỉ tắt auto no-show. - QR thành viên và QR voucher đã tách UI route; payload thành viên vẫn là local, cần signed payload/thời hạn/POS scan flow nếu dùng vận hành thật.
- Timezone boundary cho
maxAdvanceDaysđang dùngnow + N*24h; nếu nghiệp vụ hiểu là "đến hết ngày thứ 30" thì cần chỉnh rule. - Location personalization đã refetch theo
branchIdkhi match region, nhưng chưa phải lọc tọa độ/bán kính ở backend.
Ưu Tiên Đề Xuất
P0:
- Thêm CMS UI typed cho booking policy:
maxAdvanceDays,maxPartySize,reminderMinutesBefore,lateAutoCancelEnabled,lateGraceMinutes. - Nếu dùng QR thành viên để quét tại quầy: thêm signed payload API, thời hạn QR và POS/staff scan flow.
P1:
- Native QA/copy review bản dịch
ru/jatrước release. - Chốt timezone rule cho giới hạn đặt trước 30 ngày.
P2:
- Thêm contract geo-query nếu cần location đúng theo tọa độ/bán kính thay vì branchId theo region.
- Chuyển event branch filter từ client-side text location sang query server-side
branchId/branchCodenếu dữ liệu nhiều.