package outbound import ( "context" "fmt" "io" "strconv" "strings" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/batchinput/excel" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model" "github.com/gogf/gf/v2/os/gtime" ) type ExcelParseResult struct { Rows []*Row SubmitParam *model.TSimCardOutboundSubmitParam } type consistencyValue struct { rowNo int normalized string } func excelHeaders() []string { return []string{ "ICCID", "MSISDN", "IMSI", "客户/机构", "套餐", "套餐开始时间", "购买时长(年)", "赠送时长(月)", "订单金额", "线下合同/销售/走账已完成", "备注", } } func excelRequiredHeaders() []string { return []string{"ICCID"} } func ParseExcelFile(ctx context.Context, filePath string) (*ExcelParseResult, error) { parsedRows, err := excel.ParseFile(ctx, filePath, excel.ParseOptions{ RequiredHeaders: excelRequiredHeaders(), SingleSheet: true, }) if err != nil { return nil, err } return buildExcelParseResult(parsedRows), nil } func ParseExcelReader(ctx context.Context, reader io.Reader) (*ExcelParseResult, error) { parsedRows, err := excel.ParseReader(ctx, reader, excel.ParseOptions{ RequiredHeaders: excelRequiredHeaders(), SingleSheet: true, }) if err != nil { return nil, err } return buildExcelParseResult(parsedRows), nil } func buildExcelParseResult(parsedRows []excel.ParsedRow) *ExcelParseResult { rows := make([]*Row, 0, len(parsedRows)) consistentCustomerAgentId := consistencyValue{} consistentPackageCode := consistencyValue{} consistentPackageBeginTime := consistencyValue{} consistentDurationYears := consistencyValue{} consistentDurationMonthPart := consistencyValue{} consistentGiftDurationYears := consistencyValue{} consistentGiftDurationMonthPart := consistencyValue{} consistentAmount := consistencyValue{} consistentOfflineContractCompleted := consistencyValue{} consistentRemark := consistencyValue{} for _, parsed := range parsedRows { row := &Row{ RowNo: parsed.RowNo, Iccid: excel.Field(parsed, "ICCID"), Msisdn: excel.Field(parsed, "MSISDN"), Imsi: excel.Field(parsed, "IMSI"), // 出库不再配置测试期、沉默期;1 表示否。 HasTestPeriod: 1, HasSilentPeriod: 1, } parseRequiredPositiveInt(row, excelField(parsed, "customerAgentId"), "客户/机构", func(value int) { row.CustomerAgentId = int64(value) checkConsistency(row, "客户/机构", strconv.FormatInt(row.CustomerAgentId, 10), &consistentCustomerAgentId) }) row.PackageCode = strings.TrimSpace(excelField(parsed, "packageCode")) if row.PackageCode == "" { row.AddReason("套餐不能为空") } else { checkConsistency(row, "套餐", row.PackageCode, &consistentPackageCode) } parseRequiredDateTime(row, excelField(parsed, "packageBeginTime"), "套餐开始时间", func(value *gtime.Time) { row.PackageBeginTime = value checkConsistency(row, "套餐开始时间", formatTime(value), &consistentPackageBeginTime) }) parseExcelDuration(row, parsed, &consistentDurationYears, &consistentDurationMonthPart) parseExcelGiftDuration(row, parsed, &consistentGiftDurationYears, &consistentGiftDurationMonthPart) parseRequiredAmount(row, excelField(parsed, "amount"), func(value string) { row.Amount = value checkConsistency(row, "订单金额", value, &consistentAmount) }) parseRequiredBool(row, excelField(parsed, "offlineContractCompleted"), "线下合同/销售/走账已完成", func(value bool) { row.OfflineContractCompleted = value checkConsistency(row, "线下合同/销售/走账已完成", strconv.FormatBool(value), &consistentOfflineContractCompleted) }) row.Remark = strings.TrimSpace(excelField(parsed, "remark")) checkConsistency(row, "备注", row.Remark, &consistentRemark) rows = append(rows, row) } result := &ExcelParseResult{Rows: rows} if hasInvalidRows(rows) { return result } result.SubmitParam = &model.TSimCardOutboundSubmitParam{ CustomerAgentId: rows[0].CustomerAgentId, PackageCode: rows[0].PackageCode, PackageBeginTime: rows[0].PackageBeginTime, DurationYears: rows[0].DurationYears, DurationMonthPart: rows[0].DurationMonthPart, DurationMonths: rows[0].DurationMonths, GiftDurationYears: rows[0].GiftDurationYears, GiftDurationMonthPart: rows[0].GiftDurationMonthPart, GiftDurationMonths: rows[0].GiftDurationMonths, HasTestPeriod: 1, HasSilentPeriod: 1, Amount: rows[0].Amount, OfflineContractCompleted: rows[0].OfflineContractCompleted, Remark: rows[0].Remark, BuyerNote: rows[0].Remark, AdminNote: rows[0].Remark, } return result } func excelField(row excel.ParsedRow, name string) string { for _, alias := range excelFieldAliases(name) { if value := strings.TrimSpace(excel.Field(row, alias)); value != "" { return value } } return "" } func excelFieldAliases(name string) []string { switch name { case "customerAgentId": return []string{"客户/机构", "客户/机构ID", "客户ID", "customerAgentId"} case "packageCode": return []string{"套餐", "套餐编码", "packageCode"} case "packageBeginTime": return []string{"套餐开始时间", "packageBeginTime"} case "durationYears": return []string{"购买时长(年)", "套餐购买时长(年)", "durationYears"} case "durationMonthPart": return []string{"购买时长(月)", "套餐购买时长(月)", "durationMonthPart"} case "durationMonths": return []string{"durationMonths"} case "giftDurationYears": return []string{"赠送时长(年)", "giftDurationYears"} case "giftDurationMonthPart": return []string{"赠送时长(月)", "giftDurationMonthPart"} case "giftDurationMonths": return []string{"giftDurationMonths"} case "amount": return []string{"订单金额", "amount"} case "offlineContractCompleted": return []string{"线下合同/销售/走账已完成", "是否走线下", "offlineContractCompleted"} case "remark": return []string{"备注", "remark", "buyerNote", "adminNote"} default: return []string{name} } } func parseExcelDuration(row *Row, parsed excel.ParsedRow, consistentYears, consistentMonthPart *consistencyValue) { yearsRaw := excelField(parsed, "durationYears") monthPartRaw := excelField(parsed, "durationMonthPart") if strings.TrimSpace(yearsRaw) != "" || strings.TrimSpace(monthPartRaw) != "" { parseOptionalNonNegativeInt(row, yearsRaw, "购买时长(年)", 0, func(value int) { row.DurationYears = value checkConsistency(row, "购买时长(年)", strconv.Itoa(value), consistentYears) }) parseOptionalMonthPart(row, monthPartRaw, "购买时长(月)", func(value int) { row.DurationMonthPart = value checkConsistency(row, "购买时长(月)", strconv.Itoa(value), consistentMonthPart) }) row.DurationMonths = row.DurationYears*12 + row.DurationMonthPart if row.DurationMonths <= 0 { row.AddReason("购买时长必须大于0") } return } parseRequiredPositiveInt(row, excelField(parsed, "durationMonths"), "套餐购买时长(月)", func(value int) { row.DurationMonths = value row.DurationYears = value / 12 row.DurationMonthPart = value % 12 checkConsistency(row, "套餐购买时长(月)", strconv.Itoa(value), consistentMonthPart) }) } func parseExcelGiftDuration(row *Row, parsed excel.ParsedRow, consistentYears, consistentMonthPart *consistencyValue) { yearsRaw := excelField(parsed, "giftDurationYears") monthPartRaw := excelField(parsed, "giftDurationMonthPart") if strings.TrimSpace(yearsRaw) != "" || strings.TrimSpace(monthPartRaw) != "" { parseOptionalNonNegativeInt(row, yearsRaw, "赠送时长(年)", 0, func(value int) { row.GiftDurationYears = value checkConsistency(row, "赠送时长(年)", strconv.Itoa(value), consistentYears) }) parseOptionalMonthPart(row, monthPartRaw, "赠送时长(月)", func(value int) { row.GiftDurationMonthPart = value checkConsistency(row, "赠送时长(月)", strconv.Itoa(value), consistentMonthPart) }) row.GiftDurationMonths = row.GiftDurationYears*12 + row.GiftDurationMonthPart return } parseOptionalNonNegativeInt(row, excelField(parsed, "giftDurationMonths"), "赠送时长(月)", 0, func(value int) { row.GiftDurationMonths = value row.GiftDurationYears = value / 12 row.GiftDurationMonthPart = value % 12 checkConsistency(row, "赠送时长(月)", strconv.Itoa(value), consistentMonthPart) }) } func checkConsistency(row *Row, field, normalized string, base *consistencyValue) { if row == nil || !row.Valid() { return } if base.rowNo == 0 { base.rowNo = row.RowNo base.normalized = normalized return } if base.normalized != normalized { row.AddReason(fmt.Sprintf("%s与第%d行不一致", field, base.rowNo)) } } func parseRequiredPositiveInt(row *Row, raw, field string, apply func(value int)) { raw = strings.TrimSpace(raw) if raw == "" { row.AddReason(field + "不能为空") return } value, err := excel.ParsePositiveInt(raw) if err != nil { row.AddReason(field + "格式不正确") return } apply(value) } func parseOptionalNonNegativeInt(row *Row, raw, field string, defaultValue int, apply func(value int)) { raw = strings.TrimSpace(raw) if raw == "" { apply(defaultValue) return } value, err := excel.ParseNonNegativeInt(raw) if err != nil { row.AddReason(field + "格式不正确") return } apply(value) } func parseOptionalMonthPart(row *Row, raw, field string, apply func(value int)) { parseOptionalNonNegativeInt(row, raw, field, 0, func(value int) { if value > 11 { row.AddReason(field + "必须在0到11之间") return } apply(value) }) } func parseOptionalAmount(row *Row, raw string, apply func(value string)) { raw = strings.TrimSpace(raw) if raw == "" { apply("") return } value, err := excel.ParseAmount(raw) if err != nil { row.AddReason("订单金额格式不正确") return } apply(fmt.Sprintf("%.2f", value)) } func parseRequiredAmount(row *Row, raw string, apply func(value string)) { raw = strings.TrimSpace(raw) if raw == "" { row.AddReason("订单金额不能为空") return } parseOptionalAmount(row, raw, apply) } func parseRequiredBool(row *Row, raw, field string, apply func(value bool)) { raw = strings.TrimSpace(raw) if raw == "" { row.AddReason(field + "不能为空") return } value, err := excel.ParseBool(raw) if err != nil { row.AddReason(field + "格式不正确") return } apply(value) } func parseRequiredDateTime(row *Row, raw, field string, apply func(value *gtime.Time)) { raw = strings.TrimSpace(raw) if raw == "" { row.AddReason(field + "不能为空") return } value, err := excel.ParseDateTime(raw) if err != nil { row.AddReason(field + "格式不正确") return } apply(value) }