From 02d091434e68585e957fe869cec1ab4a02332edf Mon Sep 17 00:00:00 2001 From: yml2213 Date: Wed, 9 Sep 2026 15:24:56 +0800 Subject: [PATCH] =?UTF-8?q?fix(order):=20=E4=BF=AE=E5=A4=8D=E5=88=86?= =?UTF-8?q?=E9=94=80=E6=94=B6=E5=8F=B7=E5=95=86=E5=93=81=E5=9C=A8=E4=B8=BB?= =?UTF-8?q?=E7=AB=99=E7=A7=9F=E7=94=A8=E5=90=8E=E6=8A=A5=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=E4=B8=8D=E5=AD=98=E5=9C=A8=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/internal/modules/chat/conversation.go | 4 +- backend/internal/modules/chat/participant.go | 4 +- backend/internal/modules/chat/repository.go | 6 ++- .../modules/distribution/repository.go | 28 +++++++++++--- .../modules/notification/repository.go | 10 +++-- backend/internal/modules/order/queries.go | 26 ++++++++----- .../order/repository_integration_test.go | 38 ++++++++++++++++--- 7 files changed, 85 insertions(+), 31 deletions(-) diff --git a/backend/internal/modules/chat/conversation.go b/backend/internal/modules/chat/conversation.go index dc9dcf3..318240d 100644 --- a/backend/internal/modules/chat/conversation.go +++ b/backend/internal/modules/chat/conversation.go @@ -51,11 +51,11 @@ func (r *Repository) ListConversations(ctx context.Context, principal Principal, if principal.Type == "user" { if site != nil { - siteFilter := "(c.distribution_site_id = ? OR (c.order_id IS NOT NULL AND c.order_id IN (SELECT ro.id FROM rental_orders ro WHERE ro.distribution_site_id = ?)))" + siteFilter := "(c.distribution_site_id = ? OR (c.order_id IS NOT NULL AND c.order_id IN (SELECT ro.id FROM rental_orders ro WHERE ro.distribution_site_id = ? AND ro.distribution_mode != 'two_party_distributor_receive')))" countDB = countDB.Where(siteFilter, site.ID, site.ID) dataQuery = dataQuery.Where(siteFilter, site.ID, site.ID) } else { - mainFilter := "(c.distribution_site_id IS NULL OR c.distribution_site_id = 0) AND (c.order_id IS NULL OR c.order_id NOT IN (SELECT ro.id FROM rental_orders ro WHERE ro.distribution_site_id IS NOT NULL AND ro.distribution_site_id > 0))" + mainFilter := "(c.distribution_site_id IS NULL OR c.distribution_site_id = 0) AND (c.order_id IS NULL OR c.order_id NOT IN (SELECT ro.id FROM rental_orders ro WHERE ro.distribution_site_id IS NOT NULL AND ro.distribution_site_id > 0 AND ro.distribution_mode != 'two_party_distributor_receive'))" countDB = countDB.Where(mainFilter) dataQuery = dataQuery.Where(mainFilter) } diff --git a/backend/internal/modules/chat/participant.go b/backend/internal/modules/chat/participant.go index af44f06..9207410 100644 --- a/backend/internal/modules/chat/participant.go +++ b/backend/internal/modules/chat/participant.go @@ -76,10 +76,10 @@ func (r *Repository) CountUnreadMessages(ctx context.Context, principal Principa if principal.Type == "user" { if site != nil { - siteFilter := "(c.distribution_site_id = ? OR (c.order_id IS NOT NULL AND c.order_id IN (SELECT ro.id FROM rental_orders ro WHERE ro.distribution_site_id = ?)))" + siteFilter := "(c.distribution_site_id = ? OR (c.order_id IS NOT NULL AND c.order_id IN (SELECT ro.id FROM rental_orders ro WHERE ro.distribution_site_id = ? AND ro.distribution_mode != 'two_party_distributor_receive')))" query = query.Where(siteFilter, site.ID, site.ID) } else { - mainFilter := "(c.distribution_site_id IS NULL OR c.distribution_site_id = 0) AND (c.order_id IS NULL OR c.order_id NOT IN (SELECT ro.id FROM rental_orders ro WHERE ro.distribution_site_id IS NOT NULL AND ro.distribution_site_id > 0))" + mainFilter := "(c.distribution_site_id IS NULL OR c.distribution_site_id = 0) AND (c.order_id IS NULL OR c.order_id NOT IN (SELECT ro.id FROM rental_orders ro WHERE ro.distribution_site_id IS NOT NULL AND ro.distribution_site_id > 0 AND ro.distribution_mode != 'two_party_distributor_receive'))" query = query.Where(mainFilter) } } diff --git a/backend/internal/modules/chat/repository.go b/backend/internal/modules/chat/repository.go index 502a9fa..62dd8f0 100644 --- a/backend/internal/modules/chat/repository.go +++ b/backend/internal/modules/chat/repository.go @@ -87,10 +87,14 @@ func EnsureOrderConversation(tx *gorm.DB, order model.RentalOrder) (*model.ChatC return nil, err } + var siteID *uint64 + if order.DistributionSiteID != nil && *order.DistributionSiteID > 0 && order.DistributionMode != "two_party_distributor_receive" { + siteID = order.DistributionSiteID + } now := time.Now() conversation := model.ChatConversation{ OrderID: &order.ID, - DistributionSiteID: order.DistributionSiteID, + DistributionSiteID: siteID, Type: "order_group", Title: orderConversationTitle(order), Status: "active", diff --git a/backend/internal/modules/distribution/repository.go b/backend/internal/modules/distribution/repository.go index 786602c..98444f0 100644 --- a/backend/internal/modules/distribution/repository.go +++ b/backend/internal/modules/distribution/repository.go @@ -418,7 +418,7 @@ func (r *Repository) ListOrders(ctx context.Context, query DistributionOrderList size = 100 } base := r.db.WithContext(ctx).Table("rental_orders ro"). - Joins("JOIN distribution_order_financials f ON f.order_id = ro.id"). + Joins("LEFT JOIN distribution_order_financials f ON f.order_id = ro.id"). Joins("LEFT JOIN rental_listings rl ON rl.id = ro.listing_id"). Joins("LEFT JOIN game_accounts ga ON ga.id = ro.account_id"). Joins("LEFT JOIN users u ON u.id = ro.renter_id") @@ -450,8 +450,16 @@ func (r *Repository) ListOrders(ctx context.Context, query DistributionOrderList selectSQL := ` ro.id AS order_id, ro.order_no, ro.distribution_site_id AS site_id, ro.listing_id, ro.account_id, ro.owner_id, ro.renter_id, ro.status, ro.handoff_status, ro.refund_status, ro.settlement_status, ro.distribution_mode, - f.gross_paid_amount_cent, f.refund_amount_cent, f.net_paid_amount_cent, f.payment_fee_cent, f.service_fee_cent, - f.markup_amount_cent, f.tax_amount_cent, f.split_base_amount_cent, f.rounding_adjustment_cent, ro.created_at, + COALESCE(f.gross_paid_amount_cent, 0) AS gross_paid_amount_cent, + COALESCE(f.refund_amount_cent, 0) AS refund_amount_cent, + COALESCE(f.net_paid_amount_cent, 0) AS net_paid_amount_cent, + COALESCE(f.payment_fee_cent, 0) AS payment_fee_cent, + COALESCE(f.service_fee_cent, 0) AS service_fee_cent, + COALESCE(f.markup_amount_cent, 0) AS markup_amount_cent, + COALESCE(f.tax_amount_cent, 0) AS tax_amount_cent, + COALESCE(f.split_base_amount_cent, 0) AS split_base_amount_cent, + COALESCE(f.rounding_adjustment_cent, 0) AS rounding_adjustment_cent, + ro.created_at, COALESCE(rl.listing_no, '') AS listing_no, COALESCE(ga.title, '') AS title, COALESCE(ga.game_name, '') AS game_name, @@ -518,8 +526,16 @@ func (r *Repository) FindOrder(ctx context.Context, adminID, id uint64) (*Distri selectSQL := ` ro.id AS order_id, ro.order_no, ro.distribution_site_id AS site_id, ro.listing_id, ro.account_id, ro.owner_id, ro.renter_id, ro.status, ro.handoff_status, ro.refund_status, ro.settlement_status, ro.distribution_mode, - f.gross_paid_amount_cent, f.refund_amount_cent, f.net_paid_amount_cent, f.payment_fee_cent, f.service_fee_cent, - f.markup_amount_cent, f.tax_amount_cent, f.split_base_amount_cent, f.rounding_adjustment_cent, ro.created_at, + COALESCE(f.gross_paid_amount_cent, 0) AS gross_paid_amount_cent, + COALESCE(f.refund_amount_cent, 0) AS refund_amount_cent, + COALESCE(f.net_paid_amount_cent, 0) AS net_paid_amount_cent, + COALESCE(f.payment_fee_cent, 0) AS payment_fee_cent, + COALESCE(f.service_fee_cent, 0) AS service_fee_cent, + COALESCE(f.markup_amount_cent, 0) AS markup_amount_cent, + COALESCE(f.tax_amount_cent, 0) AS tax_amount_cent, + COALESCE(f.split_base_amount_cent, 0) AS split_base_amount_cent, + COALESCE(f.rounding_adjustment_cent, 0) AS rounding_adjustment_cent, + ro.created_at, COALESCE(rl.listing_no, '') AS listing_no, COALESCE(ga.title, '') AS title, COALESCE(ga.game_name, '') AS game_name, @@ -535,7 +551,7 @@ func (r *Repository) FindOrder(ctx context.Context, adminID, id uint64) (*Distri COALESCE(ro.deposit_amount_cent, 0) AS deposit_amount_cent ` err := r.db.WithContext(ctx).Table("rental_orders ro"). - Joins("JOIN distribution_order_financials f ON f.order_id = ro.id"). + Joins("LEFT JOIN distribution_order_financials f ON f.order_id = ro.id"). Joins("LEFT JOIN rental_listings rl ON rl.id = ro.listing_id"). Joins("LEFT JOIN game_accounts ga ON ga.id = ro.account_id"). Joins("LEFT JOIN users u ON u.id = ro.renter_id"). diff --git a/backend/internal/modules/notification/repository.go b/backend/internal/modules/notification/repository.go index 6b69f07..ea4349a 100644 --- a/backend/internal/modules/notification/repository.go +++ b/backend/internal/modules/notification/repository.go @@ -159,14 +159,16 @@ func Append(tx *gorm.DB, entries ...Entry) error { if siteID == nil { if (entry.BizType == "order" || entry.BizType == "order_handoff") && entry.BizID != nil && *entry.BizID > 0 { var orderSiteID *uint64 - row := tx.Table("rental_orders").Where("id = ?", *entry.BizID).Select("distribution_site_id").Row() - if err := row.Scan(&orderSiteID); err == nil && orderSiteID != nil { + var mode string + row := tx.Table("rental_orders").Where("id = ?", *entry.BizID).Select("distribution_site_id, distribution_mode").Row() + if err := row.Scan(&orderSiteID, &mode); err == nil && orderSiteID != nil && mode != "two_party_distributor_receive" { siteID = orderSiteID } } else if entry.BizType == "dispute" && entry.BizID != nil && *entry.BizID > 0 { var orderSiteID *uint64 - row := tx.Table("order_disputes d").Joins("JOIN rental_orders o ON o.id = d.order_id").Where("d.id = ?", *entry.BizID).Select("o.distribution_site_id").Row() - if err := row.Scan(&orderSiteID); err == nil && orderSiteID != nil { + var mode string + row := tx.Table("order_disputes d").Joins("JOIN rental_orders o ON o.id = d.order_id").Where("d.id = ?", *entry.BizID).Select("o.distribution_site_id, o.distribution_mode").Row() + if err := row.Scan(&orderSiteID, &mode); err == nil && orderSiteID != nil && mode != "two_party_distributor_receive" { siteID = orderSiteID } } diff --git a/backend/internal/modules/order/queries.go b/backend/internal/modules/order/queries.go index ca069fc..af8a123 100644 --- a/backend/internal/modules/order/queries.go +++ b/backend/internal/modules/order/queries.go @@ -7,6 +7,7 @@ import ( "time" "hfb_sys/backend/internal/model" + "hfb_sys/backend/internal/modules/distribution" "gorm.io/gorm" ) @@ -21,11 +22,13 @@ func (r *Repository) ListForUser(ctx context.Context, userID uint64, page, pageS code = strings.TrimSpace(siteCode[0]) } if code != "" { - base = base.Joins("INNER JOIN distribution_sites AS ds ON ds.id = o.distribution_site_id AND ds.site_code = ?", code) - countBase = countBase.Joins("INNER JOIN distribution_sites AS ds ON ds.id = o.distribution_site_id AND ds.site_code = ?", code) + base = base.Joins("INNER JOIN distribution_sites AS ds ON ds.id = o.distribution_site_id AND ds.site_code = ?", code). + Where("o.distribution_mode != ?", distribution.ModeTwoPartyDistributorReceive) + countBase = countBase.Joins("INNER JOIN distribution_sites AS ds ON ds.id = o.distribution_site_id AND ds.site_code = ?", code). + Where("o.distribution_mode != ?", distribution.ModeTwoPartyDistributorReceive) } else { - base = base.Where("o.distribution_site_id IS NULL OR o.distribution_site_id = 0") - countBase = countBase.Where("o.distribution_site_id IS NULL OR o.distribution_site_id = 0") + base = base.Where("o.distribution_site_id IS NULL OR o.distribution_site_id = 0 OR o.distribution_mode = ?", distribution.ModeTwoPartyDistributorReceive) + countBase = countBase.Where("o.distribution_site_id IS NULL OR o.distribution_site_id = 0 OR o.distribution_mode = ?", distribution.ModeTwoPartyDistributorReceive) } var total int64 @@ -64,9 +67,10 @@ func (r *Repository) ListSellerHandoffs(ctx context.Context, userID uint64, quer base = base.Where("o.status = ?", query.Status) } if query.SiteCode != "" { - base = base.Joins("INNER JOIN distribution_sites AS ds ON ds.id = o.distribution_site_id AND ds.site_code = ?", query.SiteCode) + base = base.Joins("INNER JOIN distribution_sites AS ds ON ds.id = o.distribution_site_id AND ds.site_code = ?", query.SiteCode). + Where("o.distribution_mode != ?", distribution.ModeTwoPartyDistributorReceive) } else { - base = base.Where("o.distribution_site_id IS NULL OR o.distribution_site_id = 0") + base = base.Where("o.distribution_site_id IS NULL OR o.distribution_site_id = 0 OR o.distribution_mode = ?", distribution.ModeTwoPartyDistributorReceive) } var total int64 @@ -98,9 +102,10 @@ func (r *Repository) ListSellerHandoffs(ctx context.Context, userID uint64, quer func (r *Repository) sellerHandoffMetrics(ctx context.Context, userID uint64, siteCode ...string) (SellerHandoffMetrics, error) { base := r.db.WithContext(ctx).Table("rental_orders AS o").Where("o.owner_id = ?", userID) if len(siteCode) > 0 && siteCode[0] != "" { - base = base.Joins("INNER JOIN distribution_sites AS ds ON ds.id = o.distribution_site_id AND ds.site_code = ?", siteCode[0]) + base = base.Joins("INNER JOIN distribution_sites AS ds ON ds.id = o.distribution_site_id AND ds.site_code = ?", siteCode[0]). + Where("o.distribution_mode != ?", distribution.ModeTwoPartyDistributorReceive) } else if len(siteCode) > 0 { - base = base.Where("o.distribution_site_id IS NULL OR o.distribution_site_id = 0") + base = base.Where("o.distribution_site_id IS NULL OR o.distribution_site_id = 0 OR o.distribution_mode = ?", distribution.ModeTwoPartyDistributorReceive) } var metrics SellerHandoffMetrics if err := base.Session(&gorm.Session{}).Where("o.status = ? AND o.handoff_status = ?", orderStatusPendingHandoff, handoffStatusPendingOwner).Count(&metrics.PendingHandoff).Error; err != nil { @@ -340,9 +345,10 @@ func (r *Repository) FindForUser(ctx context.Context, userID uint64, orderID uin code = strings.TrimSpace(siteCode[0]) } if code != "" { - query = query.Joins("INNER JOIN distribution_sites AS ds ON ds.id = o.distribution_site_id AND ds.site_code = ?", code) + query = query.Joins("INNER JOIN distribution_sites AS ds ON ds.id = o.distribution_site_id AND ds.site_code = ?", code). + Where("o.distribution_mode != ?", distribution.ModeTwoPartyDistributorReceive) } else if len(siteCode) > 0 { - query = query.Where("o.distribution_site_id IS NULL OR o.distribution_site_id = 0") + query = query.Where("o.distribution_site_id IS NULL OR o.distribution_site_id = 0 OR o.distribution_mode = ?", distribution.ModeTwoPartyDistributorReceive) } if err := query.First(&row).Error; err != nil { diff --git a/backend/internal/modules/order/repository_integration_test.go b/backend/internal/modules/order/repository_integration_test.go index 18bdde1..c3b2990 100644 --- a/backend/internal/modules/order/repository_integration_test.go +++ b/backend/internal/modules/order/repository_integration_test.go @@ -2018,7 +2018,7 @@ func TestListForUserAndFindForUserSiteIsolation(t *testing.T) { } db.Create(&mainOrder) - // Order 2: on distribution site (distribution_site_id = site.ID) + // Order 2: on distribution site (distribution_site_id = site.ID, sold on site) distOrder := model.RentalOrder{ OrderNo: "RO_DIST_001", ListingID: listing.ID, @@ -2027,9 +2027,23 @@ func TestListForUserAndFindForUserSiteIsolation(t *testing.T) { RenterID: renter.ID, Status: orderStatusCompleted, DistributionSiteID: &site.ID, + DistributionMode: "two_party_platform_receive", } db.Create(&distOrder) + // Order 3: received by distribution site, but rented/sold on main site + distRecvOrder := model.RentalOrder{ + OrderNo: "RO_DIST_RECV_001", + ListingID: listing.ID, + AccountID: account.ID, + OwnerID: owner.ID, + RenterID: renter.ID, + Status: orderStatusCompleted, + DistributionSiteID: &site.ID, + DistributionMode: "two_party_distributor_receive", + } + db.Create(&distRecvOrder) + ctx := context.Background() // 1. Query with site_code = "site_isolate_1": should return ONLY distOrder @@ -2041,13 +2055,13 @@ func TestListForUserAndFindForUserSiteIsolation(t *testing.T) { t.Fatalf("expected only RO_DIST_001, got %+v", distList.Items) } - // 2. Query without siteCode (main site mode): should return ONLY mainOrder + // 2. Query without siteCode (main site mode): should return mainOrder AND distRecvOrder mainList, err := repo.ListForUser(ctx, renter.ID, 1, 20, "") if err != nil { t.Fatalf("ListForUser main err: %v", err) } - if len(mainList.Items) != 1 || mainList.Items[0].OrderNo != "RO_MAIN_001" { - t.Fatalf("expected only RO_MAIN_001, got %+v", mainList.Items) + if len(mainList.Items) != 2 { + t.Fatalf("expected 2 orders on main site, got %d: %+v", len(mainList.Items), mainList.Items) } // 3. FindForUser: distOrder found under "site_isolate_1" @@ -2056,13 +2070,25 @@ func TestListForUserAndFindForUserSiteIsolation(t *testing.T) { t.Fatalf("FindForUser distOrder failed: %v", err) } - // 4. FindForUser: mainOrder CANNOT be accessed under "site_isolate_1" + // 4. FindForUser: distRecvOrder found under main site mode ("") + recvItem, err := repo.FindForUser(ctx, renter.ID, distRecvOrder.ID, "") + if err != nil || recvItem.OrderNo != "RO_DIST_RECV_001" { + t.Fatalf("FindForUser distRecvOrder failed on main site: %v", err) + } + + // 5. FindForUser: mainOrder CANNOT be accessed under "site_isolate_1" _, err = repo.FindForUser(ctx, renter.ID, mainOrder.ID, "site_isolate_1") if err == nil { t.Fatalf("expected error accessing mainOrder under distribution site, got nil") } - // 5. FindForUser: distOrder CANNOT be accessed under main site mode ("") + // 6. FindForUser: distRecvOrder CANNOT be accessed under "site_isolate_1" + _, err = repo.FindForUser(ctx, renter.ID, distRecvOrder.ID, "site_isolate_1") + if err == nil { + t.Fatalf("expected error accessing distRecvOrder under distribution site, got nil") + } + + // 7. FindForUser: distOrder CANNOT be accessed under main site mode ("") _, err = repo.FindForUser(ctx, renter.ID, distOrder.ID, "") if err == nil { t.Fatalf("expected error accessing distOrder under main site mode, got nil")