perf: reduce list query n+1
This commit is contained in:
@@ -38,10 +38,9 @@ func (s *order) ListUserOrders(ctx context.Context, userID int64, status string)
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
|
||||
var data []user_dto.Order
|
||||
for _, v := range list {
|
||||
dto, _ := s.composeOrderDTO(ctx, v)
|
||||
data = append(data, dto)
|
||||
data, err := s.composeOrderListDTO(ctx, list)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
@@ -411,6 +410,122 @@ func (s *order) composeOrderDTO(ctx context.Context, o *models.Order) (user_dto.
|
||||
return dto, nil
|
||||
}
|
||||
|
||||
func (s *order) composeOrderListDTO(ctx context.Context, orders []*models.Order) ([]user_dto.Order, error) {
|
||||
if len(orders) == 0 {
|
||||
return []user_dto.Order{}, nil
|
||||
}
|
||||
|
||||
// 批量收集订单、租户、内容 ID,避免逐条查询。
|
||||
orderIDs := make([]int64, 0, len(orders))
|
||||
tenantIDSet := make(map[int64]struct{}, len(orders))
|
||||
for _, o := range orders {
|
||||
orderIDs = append(orderIDs, o.ID)
|
||||
if o.TenantID > 0 {
|
||||
tenantIDSet[o.TenantID] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
tenantIDs := make([]int64, 0, len(tenantIDSet))
|
||||
for id := range tenantIDSet {
|
||||
tenantIDs = append(tenantIDs, id)
|
||||
}
|
||||
|
||||
tenantMap := make(map[int64]*models.Tenant, len(tenantIDs))
|
||||
if len(tenantIDs) > 0 {
|
||||
tbl, q := models.TenantQuery.QueryContext(ctx)
|
||||
list, err := q.Where(tbl.ID.In(tenantIDs...)).Find()
|
||||
if err != nil {
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
for _, t := range list {
|
||||
tenantMap[t.ID] = t
|
||||
}
|
||||
}
|
||||
|
||||
itemsByOrder := make(map[int64][]*models.OrderItem, len(orderIDs))
|
||||
contentIDSet := make(map[int64]struct{}, len(orderIDs))
|
||||
if len(orderIDs) > 0 {
|
||||
tbl, q := models.OrderItemQuery.QueryContext(ctx)
|
||||
items, err := q.Where(tbl.OrderID.In(orderIDs...)).Find()
|
||||
if err != nil {
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
for _, item := range items {
|
||||
itemsByOrder[item.OrderID] = append(itemsByOrder[item.OrderID], item)
|
||||
if item.ContentID > 0 {
|
||||
contentIDSet[item.ContentID] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contentIDs := make([]int64, 0, len(contentIDSet))
|
||||
for id := range contentIDSet {
|
||||
contentIDs = append(contentIDs, id)
|
||||
}
|
||||
|
||||
contentMap := make(map[int64]*models.Content, len(contentIDs))
|
||||
if len(contentIDs) > 0 {
|
||||
var contents []*models.Content
|
||||
tbl, q := models.ContentQuery.QueryContext(ctx)
|
||||
err := q.Where(tbl.ID.In(contentIDs...)).
|
||||
UnderlyingDB().
|
||||
Preload("ContentAssets").
|
||||
Preload("ContentAssets.Asset").
|
||||
Find(&contents).Error
|
||||
if err != nil {
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
for _, c := range contents {
|
||||
contentMap[c.ID] = c
|
||||
}
|
||||
}
|
||||
|
||||
data := make([]user_dto.Order, 0, len(orders))
|
||||
for _, o := range orders {
|
||||
dto := user_dto.Order{
|
||||
ID: o.ID,
|
||||
Type: string(o.Type),
|
||||
TypeDescription: o.Type.Description(),
|
||||
Status: string(o.Status),
|
||||
StatusDescription: o.Status.Description(),
|
||||
Amount: float64(o.AmountPaid) / 100.0,
|
||||
CreateTime: o.CreatedAt.Format(time.RFC3339),
|
||||
TenantID: o.TenantID,
|
||||
}
|
||||
|
||||
if t, ok := tenantMap[o.TenantID]; ok {
|
||||
dto.TenantName = t.Name
|
||||
}
|
||||
|
||||
items := itemsByOrder[o.ID]
|
||||
dto.Quantity = len(items)
|
||||
for _, item := range items {
|
||||
c := contentMap[item.ContentID]
|
||||
if c == nil {
|
||||
continue
|
||||
}
|
||||
ci := transaction_dto.ContentItem{
|
||||
ID: c.ID,
|
||||
Title: c.Title,
|
||||
Genre: c.Genre,
|
||||
AuthorID: c.UserID,
|
||||
Price: float64(item.AmountPaid) / 100.0,
|
||||
}
|
||||
for _, asset := range c.ContentAssets {
|
||||
if asset.Role == consts.ContentAssetRoleCover && asset.Asset != nil {
|
||||
ci.Cover = Common.GetAssetURL(asset.Asset.ObjectKey)
|
||||
break
|
||||
}
|
||||
}
|
||||
dto.Items = append(dto.Items, ci)
|
||||
}
|
||||
|
||||
data = append(data, dto)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (s *order) toUserOrderDTO(o *models.Order) user_dto.Order {
|
||||
return user_dto.Order{
|
||||
ID: o.ID,
|
||||
|
||||
Reference in New Issue
Block a user