package outbound import ( "context" "fmt" "strings" ) func validateRows(ctx context.Context, rows []*Row, opts PreviewOptions, repo *Repository) error { identityFirstRow := make(map[string]*Row, len(rows)) iccids := make([]string, 0, len(rows)) msisdns := make([]string, 0, len(rows)) imsis := make([]string, 0, len(rows)) for _, row := range rows { row.Iccid = strings.TrimSpace(row.Iccid) row.Msisdn = strings.TrimSpace(row.Msisdn) row.Imsi = strings.TrimSpace(row.Imsi) row.RangeCodeType = rowIdentityCodeType(row) key := rowIdentityKey(row) if key == "" { row.AddReason(rowIdentityLabel(row.RangeCodeType) + "不能为空") continue } identityKey := row.RangeCodeType + ":" + key if firstRow, ok := identityFirstRow[identityKey]; ok { label := rowIdentityLabel(row.RangeCodeType) row.AddReason(fmt.Sprintf("%s与第%d行重复", label, firstRow.RowNo)) firstRow.AddReason(fmt.Sprintf("%s与第%d行重复", label, row.RowNo)) continue } identityFirstRow[identityKey] = row switch row.RangeCodeType { case "msisdn": msisdns = append(msisdns, key) case "imsi": imsis = append(imsis, key) default: iccids = append(iccids, key) } } stockByIccid, err := repo.GetStockCardsByIccids(ctx, iccids) if err != nil { return err } stockByMsisdn, err := repo.GetStockCardsByMsisdns(ctx, msisdns) if err != nil { return err } stockByImsi, err := repo.GetStockCardsByImsis(ctx, imsis) if err != nil { return err } rowStock := make(map[*Row]*StockCard, len(rows)) resolvedIccidFirstRow := make(map[string]*Row, len(rows)) resolvedIccids := make([]string, 0, len(rows)) for _, row := range rows { if !row.Valid() { continue } stock := stockForRow(row, stockByIccid, stockByMsisdn, stockByImsi) if stock == nil { row.AddReason(rowIdentityLabel(row.RangeCodeType) + "不存在库存记录") continue } providedIccid := row.Iccid providedMsisdn := row.Msisdn providedImsi := row.Imsi row.Iccid = strings.TrimSpace(stock.Iccid) row.Msisdn = chooseString(row.Msisdn, stock.Msisdn) row.Imsi = chooseString(row.Imsi, stock.Imsi) if row.Iccid == "" { row.AddReason("库存卡ICCID为空") continue } if firstRow, ok := resolvedIccidFirstRow[row.Iccid]; ok { row.AddReason(fmt.Sprintf("ICCID与第%d行指向同一库存卡", firstRow.RowNo)) firstRow.AddReason(fmt.Sprintf("ICCID与第%d行指向同一库存卡", row.RowNo)) continue } resolvedIccidFirstRow[row.Iccid] = row if stock.CreatedDept != opts.DeptId { row.AddReason("ICCID不属于当前机构库存") continue } if opts.StrictStockIdentity { if providedIccid != "" && providedIccid != strings.TrimSpace(stock.Iccid) { row.AddReason("ICCID与库存记录不一致") continue } if providedMsisdn != "" && providedMsisdn != strings.TrimSpace(stock.Msisdn) { row.AddReason("MSISDN与库存记录不一致") continue } if providedImsi != "" && providedImsi != strings.TrimSpace(stock.Imsi) { row.AddReason("IMSI与库存记录不一致") continue } } row.SourceBatchNo = strings.TrimSpace(stock.BatchNo) rowStock[row] = stock resolvedIccids = append(resolvedIccids, row.Iccid) } formalByIccid, err := repo.GetExistingFormalIccids(ctx, resolvedIccids) if err != nil { return err } for _, row := range rows { if !row.Valid() || rowStock[row] == nil { continue } if formalByIccid[row.Iccid] { row.AddReason("ICCID已存在正式卡记录") } } return nil } func rowIdentityCodeType(row *Row) string { if row == nil { return "iccid" } if codeType := normalizeRangeCodeType(row.RangeCodeType); codeType != "" { return codeType } switch { case strings.TrimSpace(row.Iccid) != "": return "iccid" case strings.TrimSpace(row.Msisdn) != "": return "msisdn" case strings.TrimSpace(row.Imsi) != "": return "imsi" default: return "iccid" } } func rowIdentityKey(row *Row) string { if row == nil { return "" } switch row.RangeCodeType { case "msisdn": return strings.TrimSpace(row.Msisdn) case "imsi": return strings.TrimSpace(row.Imsi) default: return strings.TrimSpace(row.Iccid) } } func rowIdentityLabel(codeType string) string { switch codeType { case "msisdn": return "MSISDN" case "imsi": return "IMSI" default: return "ICCID" } } func stockForRow(row *Row, stockByIccid, stockByMsisdn, stockByImsi map[string]*StockCard) *StockCard { if row == nil { return nil } switch row.RangeCodeType { case "msisdn": return stockByMsisdn[strings.TrimSpace(row.Msisdn)] case "imsi": return stockByImsi[strings.TrimSpace(row.Imsi)] default: return stockByIccid[strings.TrimSpace(row.Iccid)] } }