feat: Excel 导入按列位置读取,IoT 卡导入支持卡业务类型
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m28s

- 重写 parseCardRows:移除列名识别,固定跳过第1行表头,按 col0=ICCID、
  col1=MSISDN、col2=VirtualNo 取值,VirtualNo 升级为必填字段
- 重写 ParseDeviceExcel:移除 buildDeviceColumnIndex,IMEI 调整至第5列
  新增 MaxSimSlots 范围校验 [1,4],整行为空时不计入 total
- 删除 findCardColumns 和 buildDeviceColumnIndex 两个冗余函数
- ImportIotCardRequest 新增 card_category 字段(normal/industry,默认 normal)
- IotCardImportTask model 新增 card_category 字段
- 迁移 000111:tb_iot_card_import_task 新增 card_category 列
- CreateImportTask 将 req.CardCategory 写入任务,空串兜底 normal
- processBatch 改用 task.CardCategory 替代写死的 CardCategoryNormal
- ImportTaskResponse 新增 card_category 字段返回

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-09 17:16:31 +08:00
parent ff7e749bf2
commit cd807e58e3
13 changed files with 419 additions and 152 deletions

View File

@@ -63,9 +63,10 @@ type ListStandaloneIotCardResponse struct {
}
type ImportIotCardRequest struct {
CarrierID uint `json:"carrier_id" validate:"required,min=1" required:"true" minimum:"1" description:"运营商ID"`
BatchNo string `json:"batch_no" validate:"omitempty,max=100" maxLength:"100" description:"批次号"`
FileKey string `json:"file_key" validate:"required,min=1,max=500" required:"true" minLength:"1" maxLength:"500" description:"对象存储文件路径(通过 /storage/upload-url 获取)"`
CarrierID uint `json:"carrier_id" validate:"required,min=1" required:"true" minimum:"1" description:"运营商ID"`
BatchNo string `json:"batch_no" validate:"omitempty,max=100" maxLength:"100" description:"批次号"`
FileKey string `json:"file_key" validate:"required,min=1,max=500" required:"true" minLength:"1" maxLength:"500" description:"对象存储文件路径(通过 /storage/upload-url 获取)"`
CardCategory string `json:"card_category" validate:"omitempty,oneof=normal industry" description:"卡业务类型 (normal:普通卡, industry:行业卡),默认 normal"`
}
type ImportIotCardResponse struct {
@@ -93,6 +94,7 @@ type ImportTaskResponse struct {
CarrierType string `json:"carrier_type,omitempty" description:"运营商类型 (CMCC:中国移动, CUCC:中国联通, CTCC:中国电信, CBN:中国广电)"`
CarrierName string `json:"carrier_name,omitempty" description:"运营商名称"`
BatchNo string `json:"batch_no,omitempty" description:"批次号"`
CardCategory string `json:"card_category" description:"卡业务类型 (normal:普通卡, industry:行业卡)"`
FileName string `json:"file_name,omitempty" description:"文件名"`
TotalCount int `json:"total_count" description:"总数"`
SuccessCount int `json:"success_count" description:"成功数"`

View File

@@ -31,6 +31,7 @@ type IotCardImportTask struct {
CardList CardListJSON `gorm:"column:card_list;type:jsonb;comment:待导入卡列表[{iccid,msisdn}]" json:"-"`
StorageBucket string `gorm:"column:storage_bucket;type:varchar(100);comment:对象存储桶名" json:"storage_bucket,omitempty"`
StorageKey string `gorm:"column:storage_key;type:varchar(500);comment:对象存储文件路径" json:"storage_key,omitempty"`
CardCategory string `gorm:"column:card_category;type:varchar(20);default:normal;not null;comment:卡业务类型(normal/industry)" json:"card_category"`
}
// CardItem 卡信息ICCID + MSISDN + VirtualNo

View File

@@ -70,15 +70,21 @@ func (s *Service) CreateImportTask(ctx context.Context, req *dto.ImportIotCardRe
taskNo := s.importTaskStore.GenerateTaskNo(ctx)
fileName := filepath.Base(req.FileKey)
cardCategory := req.CardCategory
if cardCategory == "" {
cardCategory = constants.CardCategoryNormal
}
task := &model.IotCardImportTask{
TaskNo: taskNo,
Status: model.ImportTaskStatusPending,
CarrierID: req.CarrierID,
CarrierType: carrier.CarrierType,
CarrierName: carrier.CarrierName,
BatchNo: req.BatchNo,
FileName: fileName,
StorageKey: req.FileKey,
TaskNo: taskNo,
Status: model.ImportTaskStatusPending,
CarrierID: req.CarrierID,
CarrierType: carrier.CarrierType,
CarrierName: carrier.CarrierName,
BatchNo: req.BatchNo,
FileName: fileName,
StorageKey: req.FileKey,
CardCategory: cardCategory,
}
task.Creator = userID
task.Updater = userID
@@ -202,6 +208,7 @@ func (s *Service) toTaskResponse(task *model.IotCardImportTask) *dto.ImportTaskR
CarrierType: task.CarrierType,
CarrierName: task.CarrierName,
BatchNo: task.BatchNo,
CardCategory: task.CardCategory,
FileName: task.FileName,
TotalCount: task.TotalCount,
SuccessCount: task.SuccessCount,

View File

@@ -354,7 +354,7 @@ func (h *IotCardImportHandler) processBatch(ctx context.Context, task *model.Iot
CarrierName: task.CarrierName,
BatchNo: task.BatchNo,
Status: constants.IotCardStatusInStock,
CardCategory: constants.CardCategoryNormal,
CardCategory: task.CardCategory,
ActivationStatus: constants.ActivationStatusInactive,
RealNameStatus: constants.RealNameStatusNotVerified,
NetworkStatus: constants.NetworkStatusOffline,