Dashboard / Telemetry — Case thường gặp
Số liệu widget lag so với dữ liệu thật
Dashboard phục vụ qua cache TTL-only, không có invalidation theo sự kiện. Số liệu có thể trễ tối đa bằng TTL của endpoint; không có hứa hẹn realtime.
Ví dụ thực tế: Staff vừa confirm xong một booking quay lại xem
BookingStatistics. → Phân biệt lag cache với sai công thức. → Cache TTL 300s → số liệu cập nhật sau tối đa 5 phút; đây là hành vi đúng thiết kế, không phải bug.
Chuyển BU vẫn thấy số của BU cũ
Site selector và mọi widget phải load lại theo BusinessUnitId mới; request với BU unknown/inactive bị reject NOT_FOUND.
Ví dụ thực tế: Admin đang xem BU "Hà Nội", đổi sang BU "Đà Nẵng". → Tránh trộn số liệu hai đơn vị. → Dashboard load lại theo BU mới; nếu BU bị deactivate → request reject
NOT_FOUND.
EstimatedRevenue lệch với tổng invoice
Revenue chỉ tính TermBooking StatusId=500 (Confirmed) + BillingId not null; 450/475 và booking chưa có hoá đơn không tính.
Ví dụ thực tế: Booking vừa confirm
StatusId=500nhưngBillingIdcòn null. → Không tính sai doanh thu dự kiến. → Booking đó chưa vàoEstimatedRevenuecho tới khi hoá đơn được gắn.
TotalBookings không giảm khi chọn khoảng ngày
TotalBookings là cộng dồn tới toDate, không phải count trong range; loại 100/900, gồm cả TermBooking và TermBookingOrder.
Ví dụ thực tế: Range 01/05–15/05 nhưng BU có 100 booking trước 01/05. → Không hiểu nhầm là số booking mới. →
TotalBookingsvẫn gồm cả 100 booking cũ; muốn số mới thì xemNewBookings.
NewBookings thiếu pending
PendingBookings chỉ lấy từ TermBookingOrder StatusId=300, không từ TermBooking.
Ví dụ thực tế: Có 2 order đang
StatusId=300nhưng không thấy trongNewBookings. → Kiểm tra đúng nguồn. →NewBookings = NewBookingsFromTermBooking + PendingBookings(từ order, status 300).
OnsiteRange ra số âm
OnsiteRange = SignedInRange − SignedOutRange; nếu SignedOutRange > SignedInRange (data anomaly) kết quả âm, không có guard ở application layer.
Ví dụ thực tế: Widget onsite hiện số âm (−2). → Đây là bất thường dữ liệu roll-call, không phải bug widget. → Kiểm tra dữ liệu
TermAttendancegốc; behavior cho số âm chưa được định nghĩa.
HourlyFlow sai giờ so với giờ vận hành
CheckInHour phải convert SignInKeyedOn (UTC) về IANA timezone của BU trước khi tách giờ; PeakStartHour/PeakEndHour dùng chung logic.
Ví dụ thực tế: BU timezone
Pacific/Auckland, check-in20:30Z(= 09:30 NZ). → Tránh lệch nửa ngày giữa UTC và giờ địa phương. → Bucket giờ 9 theo giờ BU; nếu thấy lệch → kiểm tra timezone BU đã được đặt đúng.
DST làm lệch bucket giờ
Spring-forward không được tạo bucket giờ ảo; fall-back gộp giờ lặp lại nhất quán theo quy tắc timezone chuẩn.
Ví dụ thực tế: BU ở New Zealand đêm chuyển xuân thiếu giờ 02:00–03:00. → Không được bịa giờ không tồn tại. → Không có check-in trong khoảng đó; biểu đồ không hiện bucket ảo.
Filter siteIds làm mất account/attendee
Có siteIds → join chuyển INNER JOIN; account/attendee không thuộc site được chọn bị loại hẳn. Site ngoài BU → reject cả request INVALID_ID.
Ví dụ thực tế: Account X thuộc BU nhưng chưa gán site; filter
siteIds=[2,3]. → Không được lọt vào bảng overview. → X biến mất khỏi kết quả vì joinINNER JOIN; một site sai → request reject.
SiteDistribution không đổi khi đổi fromDate
SiteDistribution trả tổng cộng dồn tại toDate; fromDate được chấp nhận nhưng vô tác dụng với query và cache key.
Ví dụ thực tế: Đổi
fromDatetừ 01/01 sang 01/03,toDategiữ nguyên. → Số liệu phải là tổng tới thời điểm hiện tại. → Kết quả không đổi vì chỉtoDateảnh hưởng; đừng đoán là bug cache.
RegistrationTrend reject groupBy lạ
groupBy chỉ chấp nhận day/week/month (case-insensitive).
Ví dụ thực tế: Gọi
groupBy=year. → Tránh grouping không định nghĩa. → Request trảINVALID_PARAMS; dùngmonthcho tương đương.
Không tìm thấy username trong telemetry
AuthenticatedUserId lấy từ name, fallback email → preferred_username → upn → sub; không có claim phù hợp → không ghi (tránh giá trị rỗng). Query theo bảng classic/workspace khác tên cột.
Ví dụ thực tế: Query workspace
AppRequeststìm theoUserAuthenticatedIdkhông ra nhưng classic có. → Chọn đúng schema của bảng đang query. → Workspace dùngUserAuthenticatedId/Properties.userName; classic dùnguser_AuthenticatedId/customDimensions.userName.
Telemetry không ghi credential
Initializer chỉ ghi username; không ghi bearer token, authorization header, claim set hay credentials.
Ví dụ thực tế: Một request lỗi kèm token. → Không rò rỉ secret vào telemetry. → Chỉ
userNamexuất hiện trong telemetry item; token/header không được lưu.
Case bổ sung: attendance hôm nay không có endpoint
IDashboardService.GetTodayAttendanceAsync đã implement nhưng không có HTTP endpoint trong DashboardController — hiện không gọi được qua public API.
Ví dụ thực tế: Dev muốn test "today attendance" qua HTTP. → Tránh tìm endpoint không tồn tại. → Method có trong service nhưng không exposed; muốn dùng phải thêm endpoint trước.