Tuần này có ba việc lớn. Một: onboard đợt 17/09 — 6 nhà, 21 phòng, 219 ảnh lên production ở trạng thái ẩn, kèm hai lỗi importer làm rơi dữ liệu trong im lặng. Hai: một đơn đặt trùng thật (CS-A9DG) và miếng vá đệm dọn hai đầu — vá xong thì vận hành báo ngược lại rằng miếng vá đang ăn mất khung bán. Ba: thiết kế lại phần sync phòng để đưa hẳn vào ChicStay backend, mới là thiết kế, chưa apply code.
Phần việc của mình ở repo chicstay-backend tuần này chỉ là sync phòng.
29 commit tính năng của repo đó (engine bán thêm giờ, quy trình xác nhận booking, Control Tower) là của
đội dev ChicStay; mình ở phía review và gộp 4 PR — ghi ở mục D.
Dry-run in ra “8 cơ sở · 0 phòng” trong khi sheet có 78 dòng phòng đã tick. Bộ nhập đọc
mã cơ sở ở cột Mã cơ sở của tab 02_Phòng, nhưng tiêu đề cột đó đang bị gõ đè thành
zZZ. Mọi dòng ra undefined rồi rơi vào một lệnh
continue câm — gói giá, album và tiện ích đều treo vào phòng nên biến mất theo.
| Dry-run | Trước | Sau khi vá |
|---|---|---|
| Phòng | 0 | 21 |
| Gói giá | 0 | 69 |
| Price rule | 0 | 141 |
| Media / tiện ích phòng | 0 / 0 | 18 / 81 |
Nay mã cơ sở được suy theo tên cột đã cắt khoảng trắng, rồi theo tiền tố CS#### của
Mã phòng, và bỏ dòng nào thì ghi vào skipped thay vì im lặng.
Lỗi thứ hai: ô “Chỉ Thứ 2 - Thứ 8” của Kat House vi phạm CHECK constraint và rollback cả lượt nạp 8
cơ sở — giá trị ngoài từ vựng nay về “Khác (ghi rõ ở Ghi chú)” kèm cảnh báo. Không đoán đó
là Thứ 5 hay Thứ 6, vì đoán sai là bán sai giá.
| Bảng | Trước | Sau |
|---|---|---|
| Merchant | 42 | 50 (+8) |
| Cơ sở | 101 | 109 (+8) |
| Phòng | 490 | 511 (+21) |
| Gói giá | 2.091 | 2.160 (+69) |
| Đã publish | 0 | 0 |
Cơ sở và phòng để DRAFT, merchant để portal_enabled=false. Kiểm cả 4 bảng:
số dòng cũ bị sửa bằng 0. Sao lưu trước khi nạp bằng Neon branch
backup-prod-before-onboard-20260917.
Chạy lại push_room_albums_to_r2.py đúng chuẩn nén cũ (WebP q80, cạnh dài ≤1600px):
500 MB ảnh gốc còn 19,5 MB, bảng media 6.239 → 6.458, không dòng cũ nào bị sửa.
Ảnh mỗi phòng: Demo 10–19, Chạm 2–9, Kat House 9–13, Biko 18–30.
Không chỉ tin script chạy xanh: quét trước 18 album (không thư mục con, video, HEIC, file >20MB); tải lại cả 219 ảnh từ R2 đối chiếu checksum/dung lượng/kích thước với DB; so nội dung từng ảnh R2 với ảnh gốc trên Drive — lệch tối đa 5/256, tức mọi ảnh đều gắn đúng phòng; mỗi phòng đúng 1 ảnh bìa.
Trong lúc kiểm tìm ra một lỗi thật: ảnh chụp dọc lên R2 thành nằm ngang — WebP không mang EXIF theo nên 2 ảnh của Chạm Dịch Vọng Hậu ra 1600×720 thay vì 720×1600. Đã vá script, 4 test tái hiện, đẩy lại 2 ảnh. Ghi thêm một chuyện tự gây ra: lúc quét album tôi tải hàng loạt ảnh gốc nên bị Google trả 403 tạm; đã dừng và chuyển sang đọc metadata Drive. Key này dùng chung với phần sync Google Sheet.
exports/chicstay-onboarding-2026-09-17.json → ra 6 cơ sở · 21
phòng · 69 gói · 219 ảnh. AN25 Hoàng Mai và Ora Hotel không có mặt vì sheet 0 dòng phòng.internal_note* trước khi đưa lên, nên mã cửa không lên ChicStay.backup-prod-before-chicstay-import-20260917; lệnh nạp chỉ chạy tiếp nếu dry-run trên
dữ liệu prod ra 0 cập nhật, 0 xoá — và kết quả đúng như vậy.hidden + is_active=False; cả 21 đã có
chicdata_room_id nên nhà nào có nguồn lịch là lịch tự chảy về, không phải sửa gì thêm.Hai chỗ Claude Code bị guard quyền chặn nên phải chuyển sang người chạy tay: bước push
main để deploy sync, và bước ẩn 6 host sau import. Hệ quả còn lại:
6 host mới vẫn is_listed=True (blue-diamond-apartment,
demo-innine, cham-cine-cafe-giang-vo, cham-cine-cafe-vong-hau,
kat-house, biko-apartment) — phòng thì ẩn nhưng host thì chưa. Nếu team muốn giấu
hẳn trong lúc review thì cần ẩn tay 6 host này.
Trả lời câu hỏi của Thu Phương: cả 8 nhà đợt này không nhà nào nối lịch tự động được —
Chạm dùng AppSheet, Ora là link booking.com, Biko là link agoda (thiếu https:// nên còn không
dựng nổi availability_config), Blue Diamond / Kat House / AN25 đều “đóng mở trực tiếp”.
Riêng DEMO Inn&Ine có sổ Google nên đã nối được (mục C2). Blue Diamond vẫn 3 phòng 0 ảnh, BD sẽ bổ sung.
| Đợt export | Host | Phòng | Gói giá | Ghi chú |
|---|---|---|---|---|
| 07/09 | 8 | 30 | 113 | đợt đầu, bản export staging |
| 08/09 (prod) | 7 | 26 | 93 | bản thật nạp production |
| 13/09 | 8 | 30 | 132 | export lại kèm dữ liệu vá trong tuần |
| 17/09 | 6 | 21 | 69 | đợt tuần này, nạp prod ở trạng thái ẩn |
Đếm trên chính chicdata theo dấu nguồn chicstay-data-sheet — tức toàn bộ những gì sheet
onboarding đã sinh ra từ trước tới nay: 31 merchant · 84 cơ sở · 423 phòng · 1.893 gói giá.
Trong đó 305 phòng đã nối được PMS (mục E); phần còn lại là nhà không có nguồn lịch tự động,
đúng như tình trạng của cả 8 nhà đợt này (mục A5).
Ngày 18/09 một khách đặt được phòng 19:00–21:00 trong khi phòng đó đã có khách Gohost nhận lúc 21:00 (đặt từ 15/09, trước đơn ChicStay ba ngày). Dựng lại mốc hôm đó:
00:00 – 01:30 CSR08910 (khách đêm trước ra 00:30 + dọn)
06:30 – 18:00 CSR08888 ở
18:00 – 19:00 dọn phòng
19:00 – 21:00 CS-A9DG ← khách ChicStay đặt lúc 10:59 ngày 18/09
21:00 – 22:00 cần dọn phòng ✗ nhưng 21:00 khách CSR08727 đã nhận
ChicStay hỏi chicdata “19:00–21:00 có bận không?” và chicdata trả lời đúng: trống — chicdata đã cộng đủ 1 giờ dọn sau mỗi khách Gohost. Chỗ hụt là ChicStay không hỏi thêm 1 giờ dọn sau đơn của chính nó; nếu hỏi “19:00–22:00” thì đơn đã bị chặn. Không phải lỗi của Bum, cũng không phải lỗi dữ liệu chicdata.
Yêu cầu vận hành 18/09 — “phòng 2–3h thì bận 1–4h” — áp cho mọi phòng của mọi nhà, thay vì chỉ cộng sau giờ trả theo cấu hình từng nhà (trước đó 281/511 phòng để 0 phút).
start_at = giờ nhận − 60, end_at = giờ trả + max(60, cấu hình nhà);
block_id vẫn tính trên khoảng GỐC nên danh tính khoảng không đổi.inventory_blocks.cleaning_before_minutes (migration 004, đã chạy production);
/v1/rooms thêm checkin_at để còn trả được giờ nhận thật.CS báo: lịch hiện khoá từ 12:00 nhưng mở PMS ra không thấy gì ở 12:00. Kiểm trên dữ liệu thật, độ dài khoá luôn bằng gói + 2 tiếng:
| Phòng | Khoá trên ChicStay | Đơn PMS suy ra |
|---|---|---|
| Oasis Blanc 23/09 | 12:00–18:00 (6h) | 13:00–17:00 = gói 4 giờ |
| Oasis Blanc 24/09 | 14:00–20:00 (6h) | 15:00–19:00 = gói 4 giờ |
| 602CL 22–23/09 | 11:00 → 13:00 hôm sau (26h) | 12:00–12:00 = gói fullday 24h |
Đệm trước giờ nhận ăn mất khung bán và không ai tra ngược ra được, vì con số đó không tồn tại bên
PMS. Luật áp cho toàn bộ 511 phòng nên không riêng Thanh Xuân — chỉ là Thanh Xuân nhiều đơn chiều nên lộ rõ.
Cần đội chốt một trong ba: (1) giữ nguyên và phổ biến quy ước “tra PMS phải cộng/trừ 1h”;
(2) bỏ đệm trước, giữ đệm sau như trước 18/09; (3) hiện lý do khoá cho ops — dữ liệu đã có sẵn trong payload
(checkin_at, cleaning_before_minutes), chỉ chưa hiển thị ở đâu.
Hai lỗi chồng nhau: phiên cache không mang cookie sang client của lượt sau nên lời gọi đầu luôn 419 rồi đăng nhập lại; và Lela mượn credential của Bum nhưng phiên giữ theo tài khoản nên hai bên đá phiên nhau mỗi phút. Số đo production 17–18/09: 1436/1441 lượt của Bum và 1437/1441 của Taphouse đều là session_renewed — gần như 100% lượt chạy phải đăng nhập lại.
Nay giữ phiên theo chủ credential, có khoá cho lượt chạy song song, chụp/nạp nguyên cookie jar. Đo lại với Gohost thật: 10 lượt Bum+Lela xen kẽ và song song → 1 lần đăng nhập, 0 session_renewed, số khoảng bận không đổi.
rtpof=true), Sheets API không đọc được. Tải qua Drive API rồi đọc bằng openpyxl, trả đúng dạng
ô như grid() để chữ ký bố cục và reader chạy chung một đường. Ngày giờ ở nằm lẫn trong ghi chú tự do, chỉ đọc
phần sau chữ “in”; không chắc thì khoá thừa (out không rõ = 24 tiếng). Soát tay 61/61
dòng của file thật khớp.| Chỗ hỏng | Hậu quả thật | Đã sửa |
|---|---|---|
| KiotViet chối quá nửa request suốt 43 phút ngày 17/09 (503 trả trong 270–485ms; 18 tài khoản khác cùng khung giờ vẫn bình thường) | Amoureux hỏng 34/45 lượt, có chuỗi 9 lượt liền, mà 153 phòng vẫn được chào bán bằng số của lần chạy được gần nhất — ngưỡng tươi 15 phút không bao giờ chạm tới vì cứ ~10 phút lại lọt một lượt | Thử lại 502/503/504 tối đa 3 lần, chỉ GET/HEAD, không thử lại khi quá hạn; hỏng ≥3 lượt liên tiếp thì hạ ảnh chụp phòng của merchant đó xuống STALE, lượt chạy được tiếp theo tự đưa lại |
openpyxl chỉ nằm ở nhóm [dev], ảnh Fly chỉ cài requirements.txt |
Miu lên production báo “No module named openpyxl”; lượt đồng bộ an toàn (PARTIAL, không đóng khoảng nào) nhưng 402 và 601 đang có khách mà chưa được khoá | Cài đủ, kèm test quét mọi import của app/ và đòi có trong requirements |
| Tiêu đề cột mã cơ sở bị gõ đè thành “zZZ” (mục A1) | 78 phòng rơi hết mà dry-run vẫn in “8 cơ sở · 0 phòng” | Suy mã cơ sở theo tên cột đã cắt khoảng trắng rồi theo tiền tố; bỏ dòng nào thì ghi skipped |
Deploy bản vá sync: push đúng commit 9c6f5a0 lên main, CI room-state xanh,
/health trả đúng 9c6f5a0. Lượt sync đầu trên bản mới: 16/16 tài khoản chạy,
15 OK, chỉ sun_sheet PARTIAL — nhưng nó PARTIAL ở mọi lượt kể cả trước deploy.
Không nhà nào bị hạ nhầm xuống “chưa biết”.
Phần việc của mình ở repo chicstay-backend tuần này chỉ là sync phòng: đưa hẳn
bộ đồng bộ lịch từ chicdata về trong backend, chạy trên một Neon dev branch. Đã viết
reports/chicstay-calendar-sync-architecture.html và chốt xong thiết kế với đội —
chưa viết spec vào repo, chưa apply một dòng code nào.
Điểm xuất phát là hai đơn thật: CS-A9DG (mục B) và CS-8D2R — phòng bugalow của ALÀ HOMESTAY, đặt
đêm 21/09, phòng này chưa map PMS nên backend coi là not_enforced và vẫn nhận đơn.
Cùng một gốc: nguồn lịch và chỗ bán phòng đang nằm ở hai hệ.
AvailabilityService.check_window() duy nhất thay vì ba nơi hiểu khác nhau./v1/rooms đứng giữa.adapter.fetch đang bay, chín người kia
chờ đúng lượt đó rồi dùng kết quả của nó. Khác dedupe ở chỗ dedupe trả dữ liệu cũ trong cửa sổ TTL,
còn single-flight trả kết quả của lượt gọi vừa xảy ra. Ca test mới trong
test_booking_external_conflict.py: mười request đồng thời → đúng một
adapter.fetch, và cả mười cùng thấy dữ liệu của lượt đó.degraded. gsheet/dayladau nhanh; gohost phải đăng nhập — phiên nằm trong Redis nên lượt sau mới
rẻ, còn lượt đầu sau khi phiên hết hạn thì phải chấp nhận chậm hoặc bỏ live check.async, Django thì không: cron asyncio.gather lấy toàn
bộ tài khoản song song, gom xong mới ghi DB đồng bộ một lượt; đường booking cũng vậy —
asyncio.run một lượt fetch, ghi overlay, rồi mới vào transaction giữ chỗ. Adapter port sang gần
như không phải sửa. Các GET/search/listing vẫn đọc DB overlay như cũ.booking_confirmation_method còn là sự thật hay chỉ là nhãn.InventorySource/InventoryRoomMapping,
giữ ExternalRoomState/ExternalBusyInterval làm read model booking).Để khỏi hiểu nhầm khi đọc git log: 29 commit tính năng của chicstay-backend tuần này
(engine booking thêm giờ, quy trình xác nhận check-in, Control Tower, ChicAI) là của đội dev
ChicStay. Phần của mình là review và gộp 4 PR, cộng với bản thiết kế sync phòng ở trên.
| Chỉ số | Tuần trước | 21/09 |
|---|---|---|
| Cơ sở trong chicdata | 101 | 109 |
| Phòng | 490 | 511 |
| Merchant | 42 | 50 |
Phòng đã nối PMS (source_mapping) | 274 | 305 |
| Cơ sở có ít nhất một phòng nối PMS | — | 57 / 109 |
| Phòng đang có khoảng bận còn hiệu lực | — | 199 |
+29 phòng nối trong tuần: 17 qua Gohost (Lela 6 phòng chạy thật sau khi vá phiên; Bum 404 Bưởi 9 và 68 CG 2 qua “mã móc lịch”) và 12 qua Google Sheet (Miu 8, DEMO Inn&Ine 4). Kiểm lại: cả 6 phòng Lela và cả 11 phòng mã móc đều đang có khoảng bận thật — hai việc treo từ tuần trước đã chạy.
Bảng của nhà dừng ở ngày 31/08, không có dòng nào cho hôm nay trở đi. Lượt đồng bộ báo PARTIAL mỗi phút từ 17/08 tới nay (9.191 lượt riêng tuần này) và 7 phòng không có một khoảng bận nào. Cần chủ home kéo dài bảng hoặc mở tab tháng mới — không sửa được bằng code.
Chờ đội chốt một trong ba hướng. Trong lúc chờ, vận hành cần biết quy ước “tra PMS phải cộng/trừ 1 giờ”, nếu không mỗi lần đối chiếu sẽ lại tưởng hệ thống sai.
21 phòng đã ẩn nhưng 6 host còn is_listed=True vì lệnh ẩn bị guard quyền chặn. Cần một người
ẩn tay, hoặc cấp quyền để chạy tiếp.
12 lượt FAILED ngày 21/09, cả ba tài khoản abri / fiora / homnaykhongngu cùng lỗi
GET /v1/listings → HTTP 502. Bộ thử lại mới đã chạy mà vẫn không qua, tức nguồn hỏng thật chứ không
phải chớp nhoáng. Đang STALE đúng thiết kế, nhưng cần theo dõi.
Nhánh feat/onboard-7-home-chuan-hoa còn 6 commit ngày 15/09 chưa có bản tương đương trên
origin/main (báo cáo sync, ghép tay phòng, script mã móc lịch, vá tiêu đề cột onboard, vá EXIF).
Cả 6 đều là script chạy tay nên production không bị ảnh hưởng — nhưng chúng đang tồn tại ở đúng
một máy và chưa ai review. Lúc deploy đã cố ý chỉ push một commit sync thay vì cả nhánh.
server/api/events/*, phụ
thuộc DB) — có trước và sau thay đổi tuần này, đang che tín hiệu CI.fb-audience-value-based.csv 995 người đúng định dạng Meta, kèm
fb-members.csv giữ nguyên 1.000 dòng gốc. File để ở Downloads, không đưa vào git để dữ
liệu cá nhân không bị commit nhầm. Chờ quyết: lưu vào DB chicdata mới trên Neon hay trên
Postgres 42.96.11.166.api.foxnagi.com),
ba endpoint /non-secure/* mở công khai, không mã hoá thân request như PlayerDuo. Thu về
20 game, 38 player (site còn mới), xuất Excel 3 sheet cho BA/BD đúng khuôn PlayerDuo, kèm
README. Review đòi đăng nhập nên bỏ qua.l-sign), signer trong repo khớp reference, nhưng gateway APISIX
h-api.lita.game vẫn trả 401. Đã đóng gói signer + README để lần sau bắt request
thật từ browser là chạy tiếp được.README.md) vẫn chưa commit
từ tuần trước.docs/superpowers/specs/ rồi mới dựng schema + native
syncer trong chicstay-backend.