perf(distribution): 优化分销订单全量与分站慢查询,消除全表扫描与每行EXISTS子查询
- 数据库迁移:新增 000083 迁移,为 distribution_order_parties 添加 (site_id, order_id) 联合覆盖索引; - 查询优化:重构 ListOrders,利用覆盖索引派生表预收敛分销订单候选集,替代在 3.7 万行 rental_orders 上的 OR EXISTS 逐行子查询全表扫描; - 边界修复:超管全量分销订单(site_id = 0)增加分销范围收敛,防止全平台普通非分销订单混杂和 5 表级联扫表; - 统计优化:同步改造 GetDashboardAnalytics 与 ListDistributionUsers,彻底消除大盘统计与客户画像聚合的慢查询; - 验证方式:新增 TestListOrdersPerformanceAndScoping 单元测试,全量运行 ./scripts/check.sh 门禁验证通过。
This commit is contained in:
@@ -1640,3 +1640,151 @@ func TestListDistributionUsersAndAnalytics(t *testing.T) {
|
||||
analytics.TodayUserCount, analytics.TotalUserCount, analytics.TotalOrderUserCount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestListOrdersPerformanceAndScoping(t *testing.T) {
|
||||
db := distributionTestDB(t)
|
||||
repo := NewRepository(db)
|
||||
|
||||
site1 := model.DistributionSite{SiteName: "站1", SiteCode: "site1", Domain: "s1.test", Status: "active"}
|
||||
site2 := model.DistributionSite{SiteName: "站2", SiteCode: "site2", Domain: "s2.test", Status: "active"}
|
||||
if err := db.Create(&site1).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.Create(&site2).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
u := model.User{Phone: "13900000001", Nickname: "买家"}
|
||||
if err := db.Create(&u).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// 1. 普通非分销订单(主站自营,不属于任何分销)
|
||||
normalOrder := model.RentalOrder{
|
||||
OrderNo: "RO_NORMAL_001",
|
||||
RenterID: u.ID,
|
||||
Status: "completed",
|
||||
DistributionSiteID: nil,
|
||||
DistributionMode: "",
|
||||
}
|
||||
if err := db.Create(&normalOrder).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// 2. 分销站1的订单
|
||||
site1ID := site1.ID
|
||||
distOrder1 := model.RentalOrder{
|
||||
OrderNo: "RO_DIST_001",
|
||||
RenterID: u.ID,
|
||||
Status: "completed",
|
||||
DistributionSiteID: &site1ID,
|
||||
DistributionMode: "site_self_sell",
|
||||
}
|
||||
if err := db.Create(&distOrder1).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.Create(&model.DistributionOrderParty{
|
||||
OrderID: distOrder1.ID,
|
||||
SiteID: site1.ID,
|
||||
PartyRole: "distributor_sell",
|
||||
PartyType: "site",
|
||||
PartyID: site1.ID,
|
||||
}).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// 3. 分销站2作为收号方的跨站订单(下单站点为0或主站,但分销站2为参与方)
|
||||
distOrder2 := model.RentalOrder{
|
||||
OrderNo: "RO_DIST_002",
|
||||
RenterID: u.ID,
|
||||
Status: "renting",
|
||||
DistributionSiteID: nil,
|
||||
DistributionMode: "two_party_distributor_receive",
|
||||
}
|
||||
if err := db.Create(&distOrder2).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.Create(&model.DistributionOrderParty{
|
||||
OrderID: distOrder2.ID,
|
||||
SiteID: site2.ID,
|
||||
PartyRole: "distributor_receive",
|
||||
PartyType: "site",
|
||||
PartyID: site2.ID,
|
||||
}).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// A. 超管查询全部分销订单 (SiteID = 0)
|
||||
// 预期:只查出 2 笔分销订单,普通主站订单 normalOrder 绝不能混入!
|
||||
allPage, err := repo.ListOrders(t.Context(), DistributionOrderListQuery{
|
||||
SiteID: 0,
|
||||
Page: 1,
|
||||
Size: 10,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("ListOrders SiteID=0 failed: %v", err)
|
||||
}
|
||||
if allPage.Total != 2 {
|
||||
t.Fatalf("expected 2 distribution orders for superadmin, got %d (normal order must be excluded)", allPage.Total)
|
||||
}
|
||||
if len(allPage.Items) != 2 {
|
||||
t.Fatalf("expected 2 items, got %d", len(allPage.Items))
|
||||
}
|
||||
|
||||
// B. 分销商1查询其订单 (SiteID = site1.ID)
|
||||
// 预期:只查出 distOrder1
|
||||
site1Page, err := repo.ListOrders(t.Context(), DistributionOrderListQuery{
|
||||
SiteID: site1.ID,
|
||||
Page: 1,
|
||||
Size: 10,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("ListOrders SiteID=1 failed: %v", err)
|
||||
}
|
||||
if site1Page.Total != 1 || site1Page.Items[0].OrderID != distOrder1.ID {
|
||||
t.Fatalf("expected order %d for site1, got total=%d items=%+v", distOrder1.ID, site1Page.Total, site1Page.Items)
|
||||
}
|
||||
|
||||
// C. 分销商2查询其订单 (SiteID = site2.ID)
|
||||
// 预期:查出 distOrder2(通过参与方命中)
|
||||
site2Page, err := repo.ListOrders(t.Context(), DistributionOrderListQuery{
|
||||
SiteID: site2.ID,
|
||||
Page: 1,
|
||||
Size: 10,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("ListOrders SiteID=2 failed: %v", err)
|
||||
}
|
||||
if site2Page.Total != 1 || site2Page.Items[0].OrderID != distOrder2.ID {
|
||||
t.Fatalf("expected order %d for site2, got total=%d items=%+v", distOrder2.ID, site2Page.Total, site2Page.Items)
|
||||
}
|
||||
|
||||
// D. 按 Kind="sold" 过滤
|
||||
soldPage, err := repo.ListOrders(t.Context(), DistributionOrderListQuery{
|
||||
SiteID: site1.ID,
|
||||
Kind: "sold",
|
||||
Page: 1,
|
||||
Size: 10,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("ListOrders Kind=sold failed: %v", err)
|
||||
}
|
||||
if soldPage.Total != 1 || soldPage.Items[0].OrderID != distOrder1.ID {
|
||||
t.Fatalf("expected 1 sold order for site1, got %d", soldPage.Total)
|
||||
}
|
||||
|
||||
// E. 按 Kind="receive" 过滤
|
||||
recvPage, err := repo.ListOrders(t.Context(), DistributionOrderListQuery{
|
||||
SiteID: site2.ID,
|
||||
Kind: "receive",
|
||||
Page: 1,
|
||||
Size: 10,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("ListOrders Kind=receive failed: %v", err)
|
||||
}
|
||||
if recvPage.Total != 1 || recvPage.Items[0].OrderID != distOrder2.ID {
|
||||
t.Fatalf("expected 1 receive order for site2, got %d", recvPage.Total)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user