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:
@@ -1063,6 +1063,22 @@ func ruleDTO(row model.DistributionRuleVersion) RuleDTO {
|
||||
return RuleDTO{ID: row.ID, SiteID: row.SiteID, Mode: row.Mode, Version: row.Version, Status: row.Status, PlatformReceiveRatio: row.PlatformReceiveRatio, PlatformSellRatio: row.PlatformSellRatio, DistributorReceiveRatio: row.DistributorReceiveRatio, DistributorSellRatio: row.DistributorSellRatio, DistributorARatio: row.DistributorARatio, DistributorBRatio: row.DistributorBRatio, ServiceFeeValue: row.ServiceFeeValue, ServiceFeeUnit: row.ServiceFeeUnit, PaymentFeeBPS: row.PaymentFeeBPS, TaxEnabled: row.TaxEnabled, TaxRateBPS: row.TaxRateBPS, EffectiveFrom: row.EffectiveFrom, EffectiveTo: row.EffectiveTo, CreatedBy: row.CreatedBy, CreatedAt: row.CreatedAt}
|
||||
}
|
||||
|
||||
const (
|
||||
siteOrderCandidatesSubquery = "(SELECT id FROM rental_orders WHERE distribution_site_id = ? UNION SELECT order_id AS id FROM distribution_order_parties WHERE site_id = ? OR party_id = ?)"
|
||||
allDistributionOrderCandidatesSubquery = "(SELECT id FROM rental_orders WHERE distribution_site_id > 0 OR distribution_mode <> '' UNION SELECT order_id AS id FROM distribution_order_parties)"
|
||||
)
|
||||
|
||||
func (r *Repository) joinDistributionOrders(db *gorm.DB, siteID uint64, orderTableAlias string) *gorm.DB {
|
||||
alias := orderTableAlias
|
||||
if alias == "" {
|
||||
alias = "rental_orders"
|
||||
}
|
||||
if siteID > 0 {
|
||||
return db.Joins("JOIN "+siteOrderCandidatesSubquery+" AS matched ON matched.id = "+alias+".id", siteID, siteID, siteID)
|
||||
}
|
||||
return db.Joins("JOIN " + allDistributionOrderCandidatesSubquery + " AS matched ON matched.id = " + alias + ".id")
|
||||
}
|
||||
|
||||
func (r *Repository) ListOrders(ctx context.Context, query DistributionOrderListQuery) (*DistributionOrderPage, error) {
|
||||
page, size := query.Page, query.Size
|
||||
if page < 1 {
|
||||
@@ -1074,15 +1090,13 @@ func (r *Repository) ListOrders(ctx context.Context, query DistributionOrderList
|
||||
if size > 100 {
|
||||
size = 100
|
||||
}
|
||||
base := r.db.WithContext(ctx).Table("rental_orders ro").
|
||||
base := r.db.WithContext(ctx).Table("rental_orders ro")
|
||||
base = r.joinDistributionOrders(base, query.SiteID, "ro")
|
||||
base = base.
|
||||
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")
|
||||
|
||||
if query.SiteID > 0 {
|
||||
base = base.Where("(ro.distribution_site_id = ? OR EXISTS (SELECT 1 FROM distribution_order_parties p WHERE p.order_id = ro.id AND (p.site_id = ? OR p.party_id = ?)))", query.SiteID, query.SiteID, query.SiteID)
|
||||
}
|
||||
if query.Status != "" {
|
||||
base = base.Where("ro.status = ?", query.Status)
|
||||
}
|
||||
@@ -3354,14 +3368,17 @@ func (r *Repository) GetDashboardAnalytics(ctx context.Context, adminID, siteID
|
||||
Where("distribution_site_id = ? AND status = 'published' AND review_status = 'approved'", siteID).Count(&activeListings).Error
|
||||
|
||||
var todayOrders int64
|
||||
_ = r.db.WithContext(ctx).Table("rental_orders ro").
|
||||
Where("(ro.distribution_site_id = ? OR EXISTS (SELECT 1 FROM distribution_order_parties p WHERE p.order_id = ro.id AND (p.site_id = ? OR p.party_id = ?))) AND ro.created_at >= ?", siteID, siteID, siteID, todayStr+" 00:00:00").
|
||||
todayOrdersQ := r.db.WithContext(ctx).Table("rental_orders ro")
|
||||
todayOrdersQ = r.joinDistributionOrders(todayOrdersQ, siteID, "ro")
|
||||
_ = todayOrdersQ.Where("ro.created_at >= ?", todayStr+" 00:00:00").
|
||||
Count(&todayOrders).Error
|
||||
|
||||
var todayGMV int64
|
||||
_ = r.db.WithContext(ctx).Table("rental_orders ro").
|
||||
todayGMVQ := r.db.WithContext(ctx).Table("rental_orders ro")
|
||||
todayGMVQ = r.joinDistributionOrders(todayGMVQ, siteID, "ro")
|
||||
_ = todayGMVQ.
|
||||
Joins("JOIN distribution_order_financials f ON f.order_id = ro.id").
|
||||
Where("(ro.distribution_site_id = ? OR EXISTS (SELECT 1 FROM distribution_order_parties p WHERE p.order_id = ro.id AND (p.site_id = ? OR p.party_id = ?))) AND ro.created_at >= ?", siteID, siteID, siteID, todayStr+" 00:00:00").
|
||||
Where("ro.created_at >= ?", todayStr+" 00:00:00").
|
||||
Select("COALESCE(SUM(f.gross_paid_amount_cent), 0)").Scan(&todayGMV).Error
|
||||
|
||||
var todayProfit int64
|
||||
@@ -3383,9 +3400,11 @@ func (r *Repository) GetDashboardAnalytics(ctx context.Context, adminID, siteID
|
||||
Where("distribution_site_id = ?", siteID).Count(&totalUserCount).Error
|
||||
|
||||
var totalOrderUserCount int64
|
||||
_ = r.db.WithContext(ctx).Table("rental_orders").
|
||||
Where("(distribution_site_id = ? OR EXISTS (SELECT 1 FROM distribution_order_parties p WHERE p.order_id = rental_orders.id AND (p.site_id = ? OR p.party_id = ?))) AND status NOT IN ('cancelled', 'closed')", siteID, siteID, siteID).
|
||||
Distinct("renter_id").Count(&totalOrderUserCount).Error
|
||||
totalOrderUserQ := r.db.WithContext(ctx).Table("rental_orders ro")
|
||||
totalOrderUserQ = r.joinDistributionOrders(totalOrderUserQ, siteID, "ro")
|
||||
_ = totalOrderUserQ.
|
||||
Where("ro.status NOT IN ('cancelled', 'closed')").
|
||||
Distinct("ro.renter_id").Count(&totalOrderUserCount).Error
|
||||
|
||||
var wallet model.WalletAccount
|
||||
_ = r.db.WithContext(ctx).Where("user_id = 0 AND distribution_site_id = ?", siteID).First(&wallet).Error
|
||||
@@ -3401,9 +3420,11 @@ func (r *Repository) GetDashboardAnalytics(ctx context.Context, adminID, siteID
|
||||
dateFormatExpr := "DATE_FORMAT(ro.created_at, '%Y-%m-%d')"
|
||||
dateFormatPartyExpr := "DATE_FORMAT(created_at, '%Y-%m-%d')"
|
||||
selectTrendSQL := fmt.Sprintf("%s AS date_str, COUNT(ro.id) AS orders, COALESCE(SUM(f.gross_paid_amount_cent), 0) AS gmv", dateFormatExpr)
|
||||
_ = r.db.WithContext(ctx).Table("rental_orders ro").
|
||||
trendQ := r.db.WithContext(ctx).Table("rental_orders ro")
|
||||
trendQ = r.joinDistributionOrders(trendQ, siteID, "ro")
|
||||
_ = trendQ.
|
||||
Joins("JOIN distribution_order_financials f ON f.order_id = ro.id").
|
||||
Where("(ro.distribution_site_id = ? OR EXISTS (SELECT 1 FROM distribution_order_parties p WHERE p.order_id = ro.id AND (p.site_id = ? OR p.party_id = ?))) AND ro.created_at >= ?", siteID, siteID, siteID, startDate+" 00:00:00").
|
||||
Where("ro.created_at >= ?", startDate+" 00:00:00").
|
||||
Select(selectTrendSQL).
|
||||
Group(dateFormatExpr).
|
||||
Order("date_str ASC").
|
||||
@@ -3503,14 +3524,18 @@ func (r *Repository) ListDistributionUsers(ctx context.Context, adminID, siteID
|
||||
_ = r.db.WithContext(ctx).Model(&model.User{}).
|
||||
Where("distribution_site_id = ?", siteID).Count(&totalUsersCount).Error
|
||||
|
||||
_ = r.db.WithContext(ctx).Table("rental_orders").
|
||||
Where("(distribution_site_id = ? OR EXISTS (SELECT 1 FROM distribution_order_parties p WHERE p.order_id = rental_orders.id AND (p.site_id = ? OR p.party_id = ?))) AND status NOT IN ('cancelled', 'closed')", siteID, siteID, siteID).
|
||||
Distinct("renter_id").Count(&orderUsersCount).Error
|
||||
orderUsersQ := r.db.WithContext(ctx).Table("rental_orders ro")
|
||||
orderUsersQ = r.joinDistributionOrders(orderUsersQ, siteID, "ro")
|
||||
_ = orderUsersQ.
|
||||
Where("ro.status NOT IN ('cancelled', 'closed')").
|
||||
Distinct("ro.renter_id").Count(&orderUsersCount).Error
|
||||
|
||||
orderSubquery := r.db.Table("rental_orders").
|
||||
Select("renter_id, COUNT(*) AS ord_count, COALESCE(SUM(rent_amount_cent), 0) AS ord_spent, MAX(created_at) AS last_ord_at").
|
||||
Where("(distribution_site_id = ? OR EXISTS (SELECT 1 FROM distribution_order_parties p WHERE p.order_id = rental_orders.id AND (p.site_id = ? OR p.party_id = ?))) AND status NOT IN ('cancelled', 'closed')", siteID, siteID, siteID).
|
||||
Group("renter_id")
|
||||
subq := r.db.Table("rental_orders ro")
|
||||
subq = r.joinDistributionOrders(subq, siteID, "ro")
|
||||
orderSubquery := subq.
|
||||
Select("ro.renter_id, COUNT(*) AS ord_count, COALESCE(SUM(ro.rent_amount_cent), 0) AS ord_spent, MAX(ro.created_at) AS last_ord_at").
|
||||
Where("ro.status NOT IN ('cancelled', 'closed')").
|
||||
Group("ro.renter_id")
|
||||
|
||||
baseQuery := r.db.WithContext(ctx).Table("users AS u").
|
||||
Joins("LEFT JOIN (?) AS ord ON ord.renter_id = u.id", orderSubquery)
|
||||
|
||||
Reference in New Issue
Block a user