fix(catalog,orders,offers): resolve listings by id, unify uploads - #17
Merged
Conversation
**"Sản phẩm không còn trong danh mục" cho sản phẩm vẫn còn**
`GET /listings?ids=` là `style: form, explode: false` — một tham số mang một
danh sách nối bằng dấu phẩy — và mọi array query param của API này đều thế.
Dio mặc định gửi ngược lại, `ids=a&ids=b&ids=c`, còn server đọc một giá trị
cho một khoá: nó trả về đúng listing đầu tiên. Lịch sử đơn resolve cả trang
bằng một lượt gọi đó, nên đơn ba dòng về với hai dòng không có listing — và
một dòng không có listing hiện đúng câu trên. `ListFormat.csv` đặt ở
`BaseOptions`, một chỗ cho cả ba tham số (`ids`, `/tags?near`,
`/categories?near`).
Fallback offline của catalog cũng trả nhầm: nó đọc một khoá `cached_listings`
toàn cục, ghi bởi lượt duyệt nào thành công gần nhất và phát lại cho lượt duyệt
nào hỏng kế tiếp — nên một trang danh mục nhận 500 lại vẽ sản phẩm của danh mục
khác, và vẽ chúng trần trụi vì bản cache dựng từ `RecentListing` vốn không mang
`category_id`. Giờ cache khoá theo bộ lọc, giữ nguyên `Listing`, và chỉ đứng ra
thay khi request không tới được server — 400/500 nổi lên như lỗi của chúng.
**Xác nhận đã nhận hàng**
Nút "Xác nhận" bị vô hiệu hoá khi chưa chọn ảnh, không kèm câu nào: bấm vào
không có gì xảy ra, không request nào rời máy. Giờ nút luôn bấm được và trả
lời. Màn chi tiết đơn cũng có nút này — trước đây phải quay lại danh sách mới
xác nhận được.
**Yêu cầu hoàn tiền**
Từ menu ba chấm — nơi nó hiện trên *mọi* đơn, kể cả đơn đã hủy và đơn đã có vụ
chưa xong, hai trường hợp server trả 409 — xuống thanh việc dưới cùng, và chỉ
hiện khi `POST /orders/{id}/refunds` thật sự nhận. Đang có vụ mở thì nút dẫn
tới vụ đó.
**Thương lượng không vượt giá niêm yết**
Chặn ở cả hai màn nhập giá, khớp với 422 `offer_above_asking` vừa thêm ở server.
**Gộp upload về một đường**
Bảy chỗ chép tay ba bước reserve → PUT → confirm, mỗi chỗ một module, và **cả
bảy đều vứt đi câu trả lời của bước confirm** — lần duy nhất server đưa ra
signed `url`, theo đúng hợp đồng. Sau khi tải lên, app chỉ còn một id, và không
route nào đổi id trần lấy ảnh: màn nào muốn khoe tấm ảnh vừa tải đều không có
gì để vẽ. `ResourceUploader` làm ba bước đó một lần cho cả năm module và trả về
`Resource` đã xác nhận; `ImageUploadField` vẽ file cục bộ cho tới khi có link
đã ký, báo lỗi từng ảnh và cho thử lại từng ảnh.
**CI**
46 bài đỏ, không bài nào là lỗi mới: fixture `Order` thiếu `refund` (đã thành
trường bắt buộc), và bốn nhóm test mô tả những màn đã được thiết kế lại — đơn
hàng giờ là sáu tab của riêng người mua, badge Tin nhắn chỉ đếm tin nhắn, trang
Tài khoản đổi nhãn, nút + mở thẳng biểu mẫu đăng bán. Test cập nhật theo thiết
kế hiện tại. 268 pass, `flutter analyze` sạch.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Vì sao sản phẩm hiện "không còn trong danh mục"
GET /listings?ids=làstyle: form, explode: false. Dio mặc định gửi ngược lại (ids=a&ids=b&ids=c) và server đọc một giá trị cho một khoá, nên nó trả về đúng listing đầu tiên. Lịch sử đơn resolve cả trang bằng một lượt gọi đó → đơn ba dòng về với hai dòng không có listing → placeholderSản phẩm không còn trong danh mụccho sản phẩm vẫn còn nguyên.Kiểm chứng trên API thật:
ids=a,b,cids=a&ids=b&ids=cSửa bằng
listFormat: ListFormat.csvởBaseOptions— một chỗ cho cảids,/tags?near,/categories?near, là toàn bộ array query param của API này.Kèm theo: fallback offline của catalog đọc một khoá
cached_listingstoàn cục, ghi bởi lượt duyệt thành công gần nhất và phát lại cho lượt duyệt hỏng kế tiếp — một trang danh mục nhận 500 lại vẽ sản phẩm của danh mục khác. Giờ cache khoá theo bộ lọc và chỉ đứng ra thay khi request không tới được server.Các thay đổi khác
ResourceUploadergộp ba bước cho cả năm module và giữ lạiResourcecủa bước confirm — signedurlchỉ được đưa ra đúng một lần, và bảy chỗ chép tay trước đây đều vứt nó đi.ImageUploadFieldlà dải ảnh dùng chung, có thử lại từng ảnh. Spec:docs/superpowers/specs/2026-08-11-unified-resource-upload-design.md.flutter analyzesạch. Không bài nào là lỗi mới — fixtureOrderthiếurefund, còn lại là test mô tả những màn đã được thiết kế lại.Lưu ý khi review
.g.dartchạy lại bằng Dart 3.12 (cú pháp mới của json_serializable). Code tay nằm ởlib/core/upload/,lib/core/network/dio_client.dart,lib/features/catalog/data/repositories/catalog_repository.dart,lib/features/account/.ImageUploadFieldchưa có test — đã thử và bỏ vì widget đọc file thật rồi đi qua Dio; lý do ghi trong spec.kyc_card_picker_widgetvàlisting_suggestion_screengiữ UI riêng (một cái là ba ô có nhãn, một cái gắn với luồng gợi ý AI) nhưng đã dùng chungResourceUploader.🤖 Generated with Claude Code