chicdata_dashboard, 13 phiên làm việc Claude trên 3 repo, một đợt gọi thử toàn bộ 173 endpoint, và số liệu vận hành truy vấn trực tiếp từ DB chicstay, chicgo + API production ngày 01/09Hai tuần trước là dựng bộ máy đồng bộ rồi mở rộng độ phủ. Tuần này chia làm hai hướng rõ rệt: ChicStay có thêm một lớp dữ liệu mới hoàn toàn (review khách từ 5 sàn OTA), còn ChicData mở nền tảng ra ngoài (khoá master + tài liệu đủ 173 endpoint).
x-api-key gọi được cả 173 endpoint, dùng đúng quy ước đã chạy ở chicstay-room-state.Sang tuần thứ năm — chicgo_data vẫn đứng im, và vẫn là con số 0. Enrich chết 32 ngày (lượt cuối 31/07), place mới dừng 23 ngày (09/08), không một lượt BD confirm nào trong cả tuần (lượt cuối 15/08). 37.232 place vẫn nằm chờ enrich. Chi tiết mục C3.
Yêu cầu đầu tuần: gom review từ mọi sàn OTA về theo cơ sở, dùng tên + địa chỉ + toạ độ để xác định trùng và ghép. Đã viết xong bộ thu thập trong chicstay-data:
| Thành phần | Nội dung |
|---|---|
| Nguồn đã viết | Booking · Agoda · Trip.com · Google Maps · Go2Joy — 5 adapter riêng |
| Bộ ghép cơ sở | reviews/match.py — ghép theo tên + địa chỉ + khoảng cách toạ độ, có match_confidence và match_method |
| Lưu trữ | Migration 0014_ota_reviews.sql — bảng ota_reviews + external_listings |
| Kiểm thử | tests/test_reviews_match.py — 187 dòng cho riêng bộ ghép |
| Quy mô | 2.528 dòng trên 11 file |
Traveloka bỏ qua có chủ ý — được đánh giá là quá khó so với giá trị mang lại, ghi lại để sau này khỏi làm lại từ đầu.
Phân bố nguồn: Google Maps 129 · Go2Joy 128 · Booking 78 · Agoda 29. Trip.com viết xong adapter nhưng chưa có review nào về — cần kiểm lại lượt chạy. Còn 56 cơ sở chưa ghép được listing nào.
Hai điểm cần xử lý sớm.
1. Toàn bộ 2.528 dòng crawler đang nằm ngoài Git — 11 file chưa commit trong chicstay-data, gồm cả migration và bộ test. Cả tuần công sức hiện chỉ tồn tại trên một máy.
2. Chạy được đúng một lượt rồi dừng. App Fly chicstay-reviews có máy reviews-run-v5 tạo lúc 03:17 ngày 31/08, chạy tới 08:21 rồi stopped. Đúng khớp mốc last_crawled_at 08:15. Chưa có lịch chạy lại, nên số review sẽ đứng yên cho tới khi chạy tay lần nữa.
Mở một cơ sở ở /chicstay → mục "Review khách". Chỉ hiện ở cấp cơ sở — review gắn với property_id chứ không gắn từng phòng.
BD sửa được mọi trường: tên người viết · ảnh đại diện · quốc gia · kiểu khách · nền tảng · điểm và thang · ngày viết · ngày ở · tên phòng · tiêu đề · nội dung · điểm cộng · điểm trừ · phản hồi chủ nhà · ảnh kèm. Cộng thêm review mới và xoá review tự nhập.
rating_5 không nhận từ client. Client gửi điểm gốc + thang, server quy đổi. Đây là cột mọi thống kê review đang dùng — cho sửa thẳng là mở đường cho biểu đồ lệch khỏi điểm thật.edited_by/edited_at — đội crawl cần bỏ qua hàng đã có người sửa, không thì lần crawl sau đè mất công BD.media — bảng đó là kho ảnh cơ sở/phòng có quy trình QA riêng.Phòng vừa có khách ra không bán lại được ngay. Từ nay khoảng bận kéo dài thêm đúng số phút dọn của từng nhà, nên API cấp cho ChicStay không còn chào bán khe giờ mà thực tế đang dọn.
| Nhà | Thời gian dọn |
|---|---|
| Bum · Abri · Fish · Amoureux · A Là | 1 giờ |
| Homnaykhongngu | 30 phút |
Trên trang đồng bộ, mỗi thanh bận trước đây liền một mạch — BD đọc ra là còn khách tới tận end_at, trong khi khách đã đi từ checkout_at và phòng chỉ đang chờ dọn, tức là tính nhầm khe trống. Nay đuôi dọn tô hổ phách kèm sọc ngược hướng với sọc chéo của "chủ khoá phòng", có chú giải trong bảng màu.
Viết script nhập dữ liệu thẳng từ Google Sheet của team vào DB (sheet có sẵn cả thông tin home lẫn link sync phòng), rồi onboard 2 home ngày 27/08:
| Home | Cơ sở | Phòng | Nguồn lịch | Tình trạng |
|---|---|---|---|---|
| La Sol Homestay M0032 |
48 Hàng Vải, Hoàn Kiếm | 4 — P102, P202, P302, P402 | pms:gsheet |
Chạy tốt — đọc lịch mỗi phút, 15 khoảng bận |
| The Palette Homestay M0033 |
35 Nguyễn Bỉnh Khiêm | 4 — Dreamy, Playful, Refined, Thoughtful | pms:gsheet qua OAuth |
Đứng 5 ngày — xem cảnh báo dưới |
Cả 8 phòng đều ghép được với nguồn lịch ở trạng thái MATCHED, không phòng nào phải soi tay.
The Palette onboard xong nhưng lịch đứng im từ 27/08 — và dashboard vẫn báo "ổn".
Sheet của The Palette chỉ chia sẻ riêng cho ecom@chicmedia.vn nên khoá Google Sheets dùng chung không mở được. Đã viết đường đọc bằng OAuth của chính hòm thư đó và deploy ngày 28/08 — nhưng secret GOOGLE_OAUTH_JSON chưa bao giờ được đặt trên Fly. Kiểm fly secrets list -a chicstay-room-state: không có biến này.
Hệ quả đo được lúc 11:19 ngày 01/09: cả 4 phòng có fetched_at = 27/08 18:50, tức đứng 5 ngày, trong khi La Sol cùng loại nguồn đọc lúc 11:19 cùng ngày. Đáng lo hơn: sync_status của cả 4 phòng vẫn là SUCCEEDED, và phòng Dreamy vẫn đang báo "bận tới 02/09 06:00" — một kết luận dựng từ dữ liệu 5 ngày tuổi.
Đúng loại lỗi đã nêu ở báo cáo tuần trước: hệ thống báo ổn trong lúc nói sai. Sửa bằng một lệnh fly secrets set GOOGLE_OAUTH_JSON=…, nhưng cảnh báo "nguồn đứng" cũng cần bắt được ca này.
Phân bố: gsheet 74 · KiotViet 71 · ezCloud 42 · GoHost 41 · GAS 20 · Dayladau 13 · Fish 4. Còn 96 liên kết chờ soi tay và 11 phòng có ảnh chụp lịch đã cũ.
Kho dữ liệu ChicStay: 29 brand · 73 cơ sở · 359 phòng · 1.898 gói giá · 5.523 ảnh. 204/359 phòng đã qua QA.
Trước tuần này API chỉ nhận cookie phiên. Hệ thống ngoài muốn lấy dữ liệu thì phải giả lập đăng nhập rồi tự giữ cookie jar — không ai làm vậy được lâu dài.
curl -H "x-api-key: $CHICDATA_API_KEY" \
"https://chicdata.fly.dev/api/places?category=cafe&page_size=20"
Không đăng nhập, không cookie, không refresh token.
Dùng lại quy ước đã có. App Fly anh em chicstay-room-state đã chạy khoá API từ trước: header x-api-key, env API_KEY cho khoá nội bộ và API_KEYS_JSON khai khoá riêng từng bên gọi. chicdata làm y hệt — bên tích hợp học một lần dùng được cả hai hệ thống.
| Biến | Ý nghĩa |
|---|---|
API_KEY | Khoá của chính đội mình, log ghi tên internal |
API_KEYS_JSON | Khoá riêng từng bên: {"doi-tac-a":"…"} — log biết ai gọi, thu hồi được đúng một bên |
API_KEY_READONLY | true = chỉ GET/HEAD, mọi lệnh ghi trả 403 |
Không phải sửa dòng nào trong 173 handler. Middleware chạy trước domain-access và maintenance, khoá hợp lệ thì dựng sẵn một phiên super-admin — mọi guard phía sau đều thấy như đã đăng nhập. Không sinh cookie, không tốn crypto mỗi request.
| Endpoint | Không gửi khoá | Có khoá |
|---|---|---|
/api/chicstay/*, products, events, chiko, ecom, scholarships | 403 | 200 |
/api/admin/users, commits, archive/places, stats/my-stats | 401 | 200 |
Đo thật trên production sau khi deploy. Hành vi cũ của dashboard không đổi: không gửi header thì mọi thứ chạy y như trước.
Mỗi endpoint có: mô tả, mức quyền, bảng tham số đường dẫn, bảng tham số query, bảng body, bảng đầu ra trải phẳng tới từng trường lồng nhau với kiểu / bắt buộc / ràng buộc / mô tả, bảng mã lỗi, và ví dụ curl chạy được ngay. Kèm Swagger UI bấm thử tại chicdata.fly.dev/api-docs/.
Sinh tự động bằng pnpm docs:api. Mục 0–5 viết tay (kiến trúc, phân quyền, quy ước, bẫy nghiệp vụ từng miền), mục 6 tự sinh và chỉ ghi đè phần sau marker — chạy bao nhiêu lần cũng không đụng phần viết tay.
pnpm test:api đọc openapi.json, tự lấy id thật từ các endpoint danh sách rồi dùng cho endpoint chi tiết, gọi lần lượt cả 173 endpoint và so phản hồi thật với schema trong tài liệu.
An toàn: dev server nối thẳng vào database production, nên mọi lệnh ghi chỉ được gọi bằng id giả và body rỗng — chạm tới lớp kiểm tra đầu vào rồi dừng. Không bao giờ ghi hay xoá dữ liệu thật.
Bản đầu chỉ so khoá tầng ngoài nên để lọt cả một tầng: /api/chicstay/reviews/listings có vỏ {rows, total} đúng nhưng mọi tên cột bên trong rows[] đều sai. Vá lại để soi cả phần tử mảng — lòi thêm 28 chỗ nữa.
| Chỗ | Tài liệu ghi | Thật ra là |
|---|---|---|
| Điểm bản đồ ChicStay | lat / lng | latitude / longitude |
| Từ khoá Ecom | id | keyword_id |
| Listing OTA | listing_id, platform | external_listing_id, source_platform |
| Thống kê category | verified, pending | 4 cột *_count |
| Lượt scrape sự kiện | found, inserted | events_scraped, events_new |
/api/stats/by-province | mảng | object {provinces, unknown, total} |
Một chỗ đáng nói riêng: trạng thái phòng. Tài liệu hứa có cột state trong phản hồi /api/chicstay/room-state. Thật ra API không hề trả cột đó — busy/free/unknown/unmapped được suy ở phía client từ busy_until + fetched_at + sync_status. Bên thứ 3 code theo tài liệu cũ sẽ đọc undefined mà không hiểu vì sao. Đây đúng là endpoint ChicStay gọi sang nhiều nhất.
Chốt lại việc chuyển từ tuần trước: chicdata, chicdata-metabase và chicstay-room-state nay chạy trên Fly, service Railway đã xoá sau khi đối chiếu ba nguồn độc lập từng phòng (Fly ↔ Railway ↔ PMS). Dọn thêm hai service chết còn giữ 21 biến mỗi cái (citycrawl dừng từ 27/07, chicgo-enrich-local dừng từ 29/07) và llm_call.
/api/stats/provinces gọi loadProvinceIndex(), hàm này chạy SELECT … FROM public.places không LIMIT, kéo cả 61.448 dòng kèm hai cột jsonb vào RAM. Máy Fly 1 GB không chịu nổi nên bị giết giữa chừng — quan sát được máy restart hai lần trong đợt gọi thử.
Endpoint này không yêu cầu đăng nhập. Bất kỳ ai trên internet gọi nó lúc cache nguội (TTL 120 giây) đều có thể làm production restart. Cùng hàm đó còn phục vụ by-province, by-category, rating-distribution, enrich-progress, reviews-timeline.
| Vấn đề | Nguyên nhân |
|---|---|
/api/places/{id}/verify-lock — 500 cả 3 method | Bảng place_verify_locks không tồn tại — chỉ có ở migration legacy/008 chưa từng chạy. Kéo theo tính năng khoá verify của BD đang chết. |
/api/stats/bd-performance — 500 | (bd_data->>'bd_score')::int gặp giá trị float "82.0". Có ở 8 chỗ trong handler. |
DELETE /api/settings/ce-environments/{id} — 500 thay vì 400 | Không kiểm id có phải số trước khi đưa vào SQL. |
| CI đỏ 100/100 lần | Không một lần xanh trong 100 lượt gần nhất, sớm nhất lấy được là 04/08 — đỏ liên tục gần một tháng. Hỏng ở bước Lint: 2.558 lỗi, trong đó 2.339 tự sửa được bằng --fix. CI không chặn deploy nên mọi commit vẫn lên production bình thường. |
chicgo_data — tuần thứ năm đứng im| Chỉ số | Lượt cuối | Đứng bao lâu |
|---|---|---|
| Enrich place | 31/07/2026 04:30 | 32 ngày |
| Enrich Perplexity | 31/07/2026 | 32 ngày |
| Place mới về | 09/08/2026 | 23 ngày |
| BD confirm | 15/08/2026 06:03 | 17 ngày |
Biểu đồ enrich 14 ngày gần nhất: 0 mỗi ngày, không sót ngày nào. Trong tuần có 0 lượt BD confirm và 0 place mới. Kho: 61.448 place, 23.891 đã enrich (39%), 37.232 đang chờ, 19.929 đã BD confirm.
Con số không nhúc nhích so với báo cáo tuần trước. Đây là tuần thứ năm liên tiếp — nếu là dừng có chủ ý thì nên ghi rõ để thôi theo dõi; nếu không thì cần người nhận việc khởi động lại.
Đo thật: /api/places?category=cafe&page_size=20 trả 984 KB dữ liệu thật — tên, địa chỉ, toạ độ, ảnh, điểm, social — không cần khoá, không cần đăng nhập. Cùng nhóm còn /api/stats/overview, /api/categories, /api/tags, /api/provinces, /api/progress/daily, /api/stats/top-places. routeRules lại bật CORS cho toàn bộ /api/**. Hành vi có sẵn từ trước, không phải do lớp khoá sinh ra.
/api/stats/provinces — endpoint công khai làm sập được production. Sửa truy vấn + thêm auth + nâng RAM.pnpm lint --fix — 2.339/2.558 lỗi tự sửa được, CI xanh lại thì cổng chất lượng mới có nghĩa.GOOGLE_OAUTH_JSON cho chicstay-room-state — 4 phòng The Palette đang phát dữ liệu 5 ngày tuổi mà vẫn báo SUCCEEDED.fetched_at cũ nhiều ngày thì hiện chưa ai báo.edited_by — không thì công BD sửa review bị đè sau mỗi lần crawl.API_KEYS_JSON + bật API_KEY_READONLY, thay vì đưa khoá của đội. Khoá master đọc được cả file mật khẩu nhân sự và client_secret của CheckEvent.chicgo_data — tuần thứ năm đứng im, cần chốt dừng hẳn hay khởi động lại.