fix: 单卡导入虚拟号改为可选,非空时保证全局唯一
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m26s

This commit is contained in:
2026-04-08 10:45:23 +08:00
parent 38ef73fd7f
commit 14a8ea5a2c
5 changed files with 35 additions and 31 deletions

View File

@@ -52,7 +52,7 @@ type IotCard struct {
AssetStatus int `gorm:"column:asset_status;type:int;not null;default:1;comment:业务状态 1-在库 2-已销售 3-已换货 4-已停用" json:"asset_status"`
Generation int `gorm:"column:generation;type:int;not null;default:1;comment:资产世代编号" json:"generation"`
IsStandalone bool `gorm:"column:is_standalone;type:boolean;default:true;not null;comment:是否为独立卡(未绑定设备) 由触发器自动维护" json:"is_standalone"`
VirtualNo string `gorm:"column:virtual_no;type:varchar(50);not null;uniqueIndex:idx_iot_card_virtual_no,where:deleted_at IS NULL;comment:虚拟号(必填,全局唯一)" json:"virtual_no"`
VirtualNo string `gorm:"column:virtual_no;type:varchar(50);uniqueIndex:idx_iot_card_virtual_no,where:deleted_at IS NULL AND virtual_no IS NOT NULL AND virtual_no <> '';comment:虚拟号(可选,非空时全局唯一)" json:"virtual_no,omitempty"`
LastGatewayReadingMB float64 `gorm:"column:last_gateway_reading_mb;type:float;not null;default:0;comment:上次轮询时网关返回的流量读数(MB)" json:"last_gateway_reading_mb"`
}

View File

@@ -305,7 +305,9 @@ func (h *IotCardImportHandler) processBatch(ctx context.Context, task *model.Iot
virtualNos := make([]string, 0, len(newCards))
for _, card := range newCards {
virtualNos = append(virtualNos, card.VirtualNo)
if card.VirtualNo != "" {
virtualNos = append(virtualNos, card.VirtualNo)
}
}
existingVirtualNos := make(map[string]bool)
existingVirtualNos, err = h.iotCardStore.ExistsByVirtualNoBatch(ctx, virtualNos)
@@ -320,17 +322,7 @@ func (h *IotCardImportHandler) processBatch(ctx context.Context, task *model.Iot
finalCards := make([]model.CardItem, 0, len(newCards))
for _, card := range newCards {
meta := cardMetaMap[card.ICCID]
if card.VirtualNo == "" {
result.failedItems = append(result.failedItems, model.ImportResultItem{
Line: meta.line,
ICCID: card.ICCID,
MSISDN: meta.msisdn,
Reason: "虚拟号(virtual_no)不能为空",
})
result.failCount++
continue
}
if existingVirtualNos[card.VirtualNo] || batchUsedVirtualNos[card.VirtualNo] {
if card.VirtualNo != "" && (existingVirtualNos[card.VirtualNo] || batchUsedVirtualNos[card.VirtualNo]) {
result.failedItems = append(result.failedItems, model.ImportResultItem{
Line: meta.line,
ICCID: card.ICCID,
@@ -378,8 +370,10 @@ func (h *IotCardImportHandler) processBatch(ctx context.Context, task *model.Iot
if err := txIdentifierStore.Register(ctx, card.ICCID, model.AssetTypeIotCard, iotCard.ID); err != nil {
return fmt.Errorf("ICCID 已被占用: %s", card.ICCID)
}
if err := txIdentifierStore.Register(ctx, card.VirtualNo, model.AssetTypeIotCard, iotCard.ID); err != nil {
return fmt.Errorf("虚拟号已被占用: %s", card.VirtualNo)
if card.VirtualNo != "" {
if err := txIdentifierStore.Register(ctx, card.VirtualNo, model.AssetTypeIotCard, iotCard.ID); err != nil {
return fmt.Errorf("虚拟号已被占用: %s", card.VirtualNo)
}
}
return nil
})