package cmd import ( "fmt" "github.com/mizuki1412/go-core-kit/init/initkit" "github.com/mizuki1412/go-core-kit/service/configkit" "github.com/spf13/cobra" "github.com/xuri/excelize/v2" "regexp" "strconv" "strings" "time" ) func init() { rootCmd.AddCommand(gcpCmd) defFlagsGcp(gcpCmd) } var gcpCmd = &cobra.Command{ Use: "gcp", Short: "Generate construction plan", Run: func(cmd *cobra.Command, args []string) { initkit.BindFlags(cmd) templatePath := configkit.GetString("gcp.template", "/Users/leo/Desktop/归档/公众扩容/在途工单通报/模板.xlsx") filePath1 := configkit.GetString("gcp.file1", "/Users/leo/Downloads/宽带在途工单进展反馈表.xlsx") filePath2 := configkit.GetString("gcp.file2", "/Users/leo/Downloads/扩容需求表.xlsx") _ = ProcessExcelData(filePath1, filePath2, templatePath) }, } func defFlagsGcp(cmd *cobra.Command) { cmd.Flags().String("gcp.template", "", "*Specify the template used to generate construction plan") cmd.Flags().String("gcp.file1", "", "*Specify the file1 used to generate construction plan") cmd.Flags().String("gcp.file2", "", "*Specify the file2 used to generate construction plan") } // ====================== 对外入口 ====================== func ProcessExcelData(broadbandPath string, expansionPath string, templatePath string) error { template, err := excelize.OpenFile(templatePath) if err != nil { return err } defer template.Close() dateFmt := "yyyy年mm月dd日" dateStyle, err := template.NewStyle(&excelize.Style{ CustomNumFmt: &dateFmt, }) if err != nil { return err } templateSheet := template.GetSheetName(0) writeRow := 2 // 模板从第2行写 today := time.Now() // ---------------- 宽带在途工单 ---------------- if err := processBroadband(template, templateSheet, broadbandPath, &writeRow, today, dateStyle); err != nil { return err } // ---------------- 扩容需求表 ---------------- if err := processExpansion(template, templateSheet, expansionPath, &writeRow, today, dateStyle); err != nil { return err } filename := time.Now().Format("20060102") + ".xlsx" return template.SaveAs("/Users/leo/Desktop/归档/公众扩容/在途工单通报/通报表格/" + filename) } // ====================== 宽带在途 ====================== func processBroadband(template *excelize.File, templateSheet string, path string, writeRow *int, today time.Time, dateStyle int) error { f, err := excelize.OpenFile(path) if err != nil { return err } defer f.Close() sheet := f.GetSheetName(0) rows, err := f.GetRows(sheet) if err != nil { return err } reDay := regexp.MustCompile(`(\d+)`) for i := 1; i < len(rows); i++ { row := rows[i] // A列 == 西湖 if getCell(row, 0) != "西湖" { continue } // O列 == 需建设处理 if getCell(row, 14) != "需建设处理" { continue } // D列 → 地址 address := getCell(row, 3) if address == "" { continue } // H列 → 填报日期 dateStr, err := f.GetCellValue(sheet, fmt.Sprintf("H%d", i+1)) if err != nil || dateStr == "" { continue } reportTime, err := parseExcelTime(dateStr) if err != nil { continue } // 写模板 template.SetCellValue(templateSheet, fmt.Sprintf("A%d", *writeRow), address) template.SetCellValue(templateSheet, fmt.Sprintf("F%d", *writeRow), reportTime) template.SetCellStyle( templateSheet, fmt.Sprintf("F%d", *writeRow), fmt.Sprintf("F%d", *writeRow), dateStyle, ) // S列:X日通 colS := getCell(row, 18) if m := reDay.FindStringSubmatch(colS); len(m) == 2 { days, _ := strconv.Atoi(m[1]) // C列:几日通 template.SetCellValue(templateSheet, fmt.Sprintf("C%d", *writeRow), days) // D列:需完成日期 finishDate := reportTime.AddDate(0, 0, days) template.SetCellValue(templateSheet, fmt.Sprintf("D%d", *writeRow), finishDate) template.SetCellStyle( templateSheet, fmt.Sprintf("D%d", *writeRow), fmt.Sprintf("D%d", *writeRow), dateStyle, ) // E列:是否超时 if today.After(finishDate) { template.SetCellValue(templateSheet, fmt.Sprintf("E%d", *writeRow), "是") } else { template.SetCellValue(templateSheet, fmt.Sprintf("E%d", *writeRow), "否") } } // G列:工单历时 duration := int(today.Sub(reportTime).Hours() / 24) if duration < 0 { duration = 0 } template.SetCellValue(templateSheet, fmt.Sprintf("G%d", *writeRow), duration) *writeRow++ } return nil } // ====================== 扩容需求 ====================== func processExpansion(template *excelize.File, templateSheet string, path string, writeRow *int, today time.Time, dateStyle int) error { f, err := excelize.OpenFile(path) if err != nil { return err } defer f.Close() sheet := f.GetSheetName(0) rows, err := f.GetRows(sheet) if err != nil { return err } for i := 1; i < len(rows); i++ { row := rows[i] // P列为空 if strings.TrimSpace(getCell(row, 15)) != "" { continue } // X列 == 1 if getCell(row, 23) != "1" { continue } //D列 → 地址 address := getCell(row, 3) if address == "" { continue } // N列 → 填报日期 dateStr, err := f.GetCellValue(sheet, fmt.Sprintf("N%d", i+1)) if err != nil || dateStr == "" { continue } reportTime, err := parseExcelTime(dateStr) if err != nil { continue } template.SetCellValue(templateSheet, fmt.Sprintf("A%d", *writeRow), address) template.SetCellValue(templateSheet, fmt.Sprintf("F%d", *writeRow), reportTime) template.SetCellStyle( templateSheet, fmt.Sprintf("F%d", *writeRow), fmt.Sprintf("F%d", *writeRow), dateStyle, ) // G列:工单历时 duration := int(today.Sub(reportTime).Hours() / 24) if duration < 0 { duration = 0 } template.SetCellValue(templateSheet, fmt.Sprintf("G%d", *writeRow), duration) *writeRow++ } return nil } // ====================== 工具函数 ====================== func parseExcelTime(val string) (time.Time, error) { val = strings.TrimSpace(val) if val == "" { return time.Time{}, fmt.Errorf("empty time") } // 1️⃣ Excel 原生数值(最优先) if f, err := strconv.ParseFloat(val, 64); err == nil { return excelize.ExcelDateToTime(f, false) } // 2️⃣ 中文日期:11月23日 / 11月23号 reCN := regexp.MustCompile(`^(\d{1,2})月(\d{1,2})([日号])?$`) if m := reCN.FindStringSubmatch(val); len(m) > 0 { month, _ := strconv.Atoi(m[1]) day, _ := strconv.Atoi(m[2]) //todo year := 0 if month >= 11 { year = 2025 // 默认用当前年 } year = time.Now().Year() // 默认用当前年 return time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.Local), nil } // 3️⃣ 常见字符串格式 layouts := []string{ "2006-01-02", "2006/01/02", "2006-01-02 15:04", "2006/01/02 15:04", "2006-01-02 15:04:05", "2006/01/02 15:04:05", } for _, layout := range layouts { if t, err := time.ParseInLocation(layout, val, time.Local); err == nil { return t, nil } } return time.Time{}, fmt.Errorf("cannot parse time: %s", val) } // 安全取单元格 func getCell(row []string, idx int) string { if idx >= len(row) { return "" } return strings.TrimSpace(row[idx]) }