feat: 给 PackageUsage 添加 package_name 快照字段,修复企业卡列表 carrier_name/package_name 为空问题

- 迁移 000099:tb_package_usage 新增 package_name 列,回填历史数据
- PackageUsage model 添加 PackageName 字段(与 DataResetCycle 保持一致的快照设计)
- 4 处 PackageUsage 创建点补充 PackageName 快照(order/service.go + auto_purchase.go)
- EnterpriseCard ListCards:carrier_name 直接取 card.CarrierName,package_name 通过
  DISTINCT ON 批量查当前生效主套餐,单次查询覆盖整页,无 N+1
This commit is contained in:
2026-03-31 12:43:07 +08:00
parent 5b283285a2
commit 121462c00f
6 changed files with 61 additions and 0 deletions

View File

@@ -288,6 +288,13 @@ func (s *Service) ListCards(ctx context.Context, enterpriseID uint, req *dto.Ent
return nil, errors.Wrap(errors.CodeInternalError, err, "查询卡列表失败")
}
// 批量查询各卡当前生效的主套餐名称(取 package_name 快照,避免 JOIN tb_package
pageCardIDs := make([]uint, 0, len(cards))
for _, card := range cards {
pageCardIDs = append(pageCardIDs, card.ID)
}
packageNameMap := s.buildPackageNameMap(ctx, pageCardIDs)
items := make([]dto.EnterpriseCardItem, 0, len(cards))
for _, card := range cards {
items = append(items, dto.EnterpriseCardItem{
@@ -295,6 +302,8 @@ func (s *Service) ListCards(ctx context.Context, enterpriseID uint, req *dto.Ent
ICCID: card.ICCID,
MSISDN: card.MSISDN,
CarrierID: card.CarrierID,
CarrierName: card.CarrierName,
PackageName: packageNameMap[card.ID],
Status: card.Status,
StatusName: getCardStatusName(card.Status),
NetworkStatus: card.NetworkStatus,
@@ -310,6 +319,36 @@ func (s *Service) ListCards(ctx context.Context, enterpriseID uint, req *dto.Ent
}, nil
}
// buildPackageNameMap 批量查询卡列表当前生效主套餐名称,返回 map[iotCardID]packageName
func (s *Service) buildPackageNameMap(ctx context.Context, cardIDs []uint) map[uint]string {
result := make(map[uint]string, len(cardIDs))
if len(cardIDs) == 0 {
return result
}
type usageRow struct {
IotCardID uint `gorm:"column:iot_card_id"`
PackageName string `gorm:"column:package_name"`
}
// 每张卡取优先级最低(数字最小)的生效中主套餐,使用 DISTINCT ON 保证每卡仅一条
var rows []usageRow
s.db.WithContext(ctx).Raw(`
SELECT DISTINCT ON (iot_card_id) iot_card_id, package_name
FROM tb_package_usage
WHERE iot_card_id IN ?
AND status = 1
AND master_usage_id IS NULL
AND deleted_at IS NULL
ORDER BY iot_card_id, priority ASC
`, cardIDs).Scan(&rows)
for _, row := range rows {
result[row.IotCardID] = row.PackageName
}
return result
}
func (s *Service) SuspendCard(ctx context.Context, enterpriseID, cardID uint) error {
return s.updateCardNetworkStatus(ctx, enterpriseID, cardID, 0)
}