Files
junhong_cmp_fiber/pkg/utils/csv.go
huang 45aa7deb87
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m33s
feat: 添加环境变量管理工具和部署配置改版
主要改动:
- 新增交互式环境配置脚本 (scripts/setup-env.sh)
- 新增本地启动快捷脚本 (scripts/run-local.sh)
- 新增环境变量模板文件 (.env.example)
- 部署模式改版:使用嵌入式配置 + 环境变量覆盖
- 添加对象存储功能支持
- 改进 IoT 卡片导入任务
- 优化 OpenAPI 文档生成
- 删除旧的配置文件,改用嵌入式默认配置
2026-01-26 10:28:29 +08:00

133 lines
2.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package utils
import (
"encoding/csv"
"errors"
"io"
"strings"
)
// CardInfo 卡信息ICCID + MSISDN
type CardInfo struct {
ICCID string
MSISDN string
}
// CSVParseResult CSV 解析结果
type CSVParseResult struct {
Cards []CardInfo
TotalCount int
ParseErrors []CSVParseError
}
// CSVParseError CSV 解析错误
type CSVParseError struct {
Line int
ICCID string
MSISDN string
Reason string
}
// ErrInvalidCSVFormat CSV 格式错误
var ErrInvalidCSVFormat = errors.New("CSV 文件格式错误:缺少 MSISDN 列,文件必须包含 ICCID 和 MSISDN 两列")
// ParseCardCSV 解析包含 ICCID 和 MSISDN 两列的 CSV 文件
func ParseCardCSV(reader io.Reader) (*CSVParseResult, error) {
csvReader := csv.NewReader(reader)
csvReader.FieldsPerRecord = -1
csvReader.TrimLeadingSpace = true
result := &CSVParseResult{
Cards: make([]CardInfo, 0),
ParseErrors: make([]CSVParseError, 0),
}
lineNum := 0
headerSkipped := false
for {
record, err := csvReader.Read()
if err == io.EOF {
break
}
lineNum++
if err != nil {
result.ParseErrors = append(result.ParseErrors, CSVParseError{
Line: lineNum,
Reason: "CSV 解析错误: " + err.Error(),
})
continue
}
if len(record) == 0 {
continue
}
if len(record) < 2 {
if lineNum == 1 && !headerSkipped {
firstCol := strings.TrimSpace(record[0])
if isICCIDHeader(firstCol) {
return nil, ErrInvalidCSVFormat
}
}
result.ParseErrors = append(result.ParseErrors, CSVParseError{
Line: lineNum,
ICCID: strings.TrimSpace(record[0]),
Reason: "列数不足:缺少 MSISDN 列",
})
result.TotalCount++
continue
}
iccid := strings.TrimSpace(record[0])
msisdn := strings.TrimSpace(record[1])
if lineNum == 1 && !headerSkipped && isHeader(iccid, msisdn) {
headerSkipped = true
continue
}
result.TotalCount++
if iccid == "" {
result.ParseErrors = append(result.ParseErrors, CSVParseError{
Line: lineNum,
MSISDN: msisdn,
Reason: "ICCID 不能为空",
})
continue
}
if msisdn == "" {
result.ParseErrors = append(result.ParseErrors, CSVParseError{
Line: lineNum,
ICCID: iccid,
Reason: "MSISDN 不能为空",
})
continue
}
result.Cards = append(result.Cards, CardInfo{
ICCID: iccid,
MSISDN: msisdn,
})
}
return result, nil
}
func isICCIDHeader(value string) bool {
lower := strings.ToLower(value)
return lower == "iccid" || lower == "卡号" || lower == "号码"
}
func isMSISDNHeader(value string) bool {
lower := strings.ToLower(value)
return lower == "msisdn" || lower == "接入号" || lower == "手机号" || lower == "电话" || lower == "号码"
}
func isHeader(col1, col2 string) bool {
return isICCIDHeader(col1) && isMSISDNHeader(col2)
}