package jobs import ( "bytes" "encoding/json" "fmt" "fwh-toolbox-server/domain" "fwh-toolbox-server/dto" function "fwh-toolbox-server/function/SnapFile" "fwh-toolbox-server/repositories" "fwh-toolbox-server/utils/echart" "fwh-toolbox-server/utils/mimetype" "fwh-toolbox-server/utils/minio" "os" "path/filepath" "regexp" "sort" "strconv" "strings" "time" "github.com/go-echarts/go-echarts/v2/opts" "github.com/xuri/excelize/v2" "go.uber.org/zap" ) /* 文件服务器存储结构 * BatchDownloadData/ * └── `SN`/ * ├── PowerFlow/ 功率流图文件 * ├── MQTT/ MQTT通讯数据文件 * ├── EventAlarm/ 事件日志与告警日志文件 * ├── AlarmSnapshot 告警快照 * ├── EmsLogs/ EMS日志文件 * ├── FaultRecord/ 故障录波文件 * ├── UpdateLog/ 升级日志数据文件 * └── Settings/ 设置数据文件 * * BatchDownloadPackage/ 任务打包数据 */ var ( mainPathName string = "BatchDownloadData" subPowerFlowPathName string = "PowerFlow" subMqttPathName string = "MQTT" subEventAlarmPathName string = "EventAlarm" subAlarmSnapshotPathName string = "AlarmSnapshot" subEmsLogsPathName string = "EmsLogs" subFaultRecordPathName string = "FaultRecord" subUpdateLogPathName string = "UpdateLog" subSettingsPathName string = "Settings" ) func stringPtr(s string) *string { return &s } // 创建临时保存路径 func createTempPath(pathType string) (string, error) { // 创建临时目录存储下载和解压数据 tempDirName := fmt.Sprintf("fwhtb-batchDownload-%s-*", pathType) tempDir, err := os.MkdirTemp("", tempDirName) if err != nil { zap.L().Error("创建临时目录失败", zap.String("pathName", tempDirName), zap.String("err", err.Error())) return "", err } return tempDir, nil } // Excel 指定行-列写入数据, 行列从1开始 func excelSetCellValue(f *excelize.File, sheetName string, col int, row int, val interface{}) error { colName, err := excelize.ColumnNumberToName(col) if err != nil { zap.L().Error("Excel 列标转换失败", zap.String("col", strconv.Itoa(col)), zap.String("err", err.Error())) return err } return f.SetCellValue(sheetName, fmt.Sprintf("%s%d", colName, row), val) } // 判断功率流图文件是否存在 func ExistsBatchDownloadPowerFlow(sn string, dateStr string) (bool, error) { fileName := fmt.Sprintf("PowerFlow_%s.html", strings.ReplaceAll(dateStr, "-", "")) objectName := fmt.Sprintf("%s/%s/%s/%s", mainPathName, sn, subPowerFlowPathName, fileName) exists, err := minio.ExistsObject(objectName) if err != nil { zap.L().Error("检查PowerFlow文件失败", zap.String("fileName", fileName), zap.String("objectName", objectName), zap.String("err", err.Error())) return false, err } return exists, nil } // 获取功率流图文件状态信息 func StatBatchDownloadPowerFlow(sn string, dateStr string) (*minio.ObjectInfo, error) { fileName := fmt.Sprintf("PowerFlow_%s.html", strings.ReplaceAll(dateStr, "-", "")) objectName := fmt.Sprintf("%s/%s/%s/%s", mainPathName, sn, subPowerFlowPathName, fileName) status, err := minio.StatObject(objectName) if err != nil { zap.L().Error("获取PowerFlow文件状态失败", zap.String("fileName", fileName), zap.String("objectName", objectName), zap.String("err", err.Error())) return nil, err } return &status, nil } // 保存功率流图到文件服务器 // return objName func SaveBatchDownloadPowerFlow(sn string, dateStr string, powerFlowData dto.SitePowerFlow) (string, error) { // 创建临时保存文件路径 tempPath, err := createTempPath("powerFlow") if err != nil { return "", err } defer os.RemoveAll(tempPath) // 程序结束时清理临时目录 fileName := fmt.Sprintf("PowerFlow_%s.html", strings.ReplaceAll(dateStr, "-", "")) filePath := filepath.Join(tempPath, fileName) // 数据结构转换 pvData := []opts.LineData{} genData := []opts.LineData{} gridData := []opts.LineData{} aPowerData := []opts.LineData{} loadData := []opts.LineData{} socData := []opts.LineData{} for i, t := range powerFlowData.DeviceTimeArray { var pvValue []interface{} if i < len(powerFlowData.PowerSunArray) { pvValue = []interface{}{ t, powerFlowData.PowerSunArray[i], } } else { pvValue = []interface{}{ t, nil, } } pvData = append(pvData, opts.LineData{ Value: pvValue, }) var genValue []interface{} if i < len(powerFlowData.PowerGenArray) { genValue = []interface{}{ t, powerFlowData.PowerGenArray[i], } } else { genValue = []interface{}{ t, nil, } } genData = append(genData, opts.LineData{ Value: genValue, }) var gridValue []interface{} if i < len(powerFlowData.PowerUtiArray) { gridValue = []interface{}{ t, powerFlowData.PowerUtiArray[i], } } else { gridValue = []interface{}{ t, nil, } } gridData = append(gridData, opts.LineData{ Value: gridValue, }) var aPowerValue []interface{} if i < len(powerFlowData.PowerFhpArray) { aPowerValue = []interface{}{ t, powerFlowData.PowerFhpArray[i], } } else { aPowerValue = []interface{}{ t, nil, } } aPowerData = append(aPowerData, opts.LineData{ Value: aPowerValue, }) var loadValue []interface{} if i < len(powerFlowData.PowerLoadArray) { loadValue = []interface{}{ t, powerFlowData.PowerLoadArray[i], } } else { loadValue = []interface{}{ t, nil, } } loadData = append(loadData, opts.LineData{ Value: loadValue, }) // SOC var socValue []interface{} if i < len(powerFlowData.SocArray) { socValue = []interface{}{ t, powerFlowData.SocArray[i], } } else { socValue = []interface{}{ t, nil, } } socData = append(socData, opts.LineData{ Value: socValue, }) } // 生成功率流图 var pfChartData []echart.PowerFlowChartData = []echart.PowerFlowChartData{} pfChartData = append(pfChartData, echart.PowerFlowChartData{ Legend: "光伏", Color: stringPtr("#F1A502"), Data: pvData, }) pfChartData = append(pfChartData, echart.PowerFlowChartData{ Legend: "发电机", Color: stringPtr("#5398FF"), Data: genData, }) pfChartData = append(pfChartData, echart.PowerFlowChartData{ Legend: "市电", Color: stringPtr("#BB56FD"), Data: gridData, }) pfChartData = append(pfChartData, echart.PowerFlowChartData{ Legend: "aPower", Color: stringPtr("#41D187"), Data: aPowerData, }) pfChartData = append(pfChartData, echart.PowerFlowChartData{ Legend: "家庭负载", Color: stringPtr("#0BF5FF"), Data: loadData, }) var socChartData echart.SocChartData = echart.SocChartData{ Color: stringPtr("#3E7B4D"), Data: socData, } lineOpt := echart.LineChartOpt{ Global: echart.ChartOpt{ Title: "功率流图", }, Smooth: true, } line := echart.CreatePowerFlowChart(lineOpt, pfChartData, socChartData) f, _ := os.Create(filePath) defer f.Close() line.Render(f) // 上传到文件服务器 objectName := fmt.Sprintf("%s/%s/%s/%s", mainPathName, sn, subPowerFlowPathName, fileName) if err = minio.UploadFile(filePath, objectName); err != nil { zap.L().Error("上传PowerFlow文件失败", zap.String("path", filePath), zap.String("objectName", objectName), zap.String("err", err.Error())) return "", err } return objectName, nil } // 判断MqttLog excel文件是否存在 func ExistsBatchDownloadMqttLog(sn string, cmdType string, dateStr string) (bool, error) { fileName := fmt.Sprintf("MqttLog_%s_%s.xlsx", cmdType, strings.ReplaceAll(dateStr, "-", "")) objectName := fmt.Sprintf("%s/%s/%s/%s", mainPathName, sn, subMqttPathName, fileName) exists, err := minio.ExistsObject(objectName) if err != nil { zap.L().Error("检查MqttLog excel文件失败", zap.String("fileName", fileName), zap.String("objectName", objectName), zap.String("err", err.Error())) return false, err } return exists, nil } // 获取MqttLog excel文件状态信息 func StatBatchDownloadMqttLog(sn string, cmdType string, dateStr string) (*minio.ObjectInfo, error) { fileName := fmt.Sprintf("MqttLog_%s_%s.xlsx", cmdType, strings.ReplaceAll(dateStr, "-", "")) objectName := fmt.Sprintf("%s/%s/%s/%s", mainPathName, sn, subMqttPathName, fileName) status, err := minio.StatObject(objectName) if err != nil { zap.L().Error("获取MqttLog excel文件状态失败", zap.String("fileName", fileName), zap.String("objectName", objectName), zap.String("err", err.Error())) return nil, err } return &status, nil } // 保存MqttLog excel到文件服务器 // return objName func SaveBatchDownloadMqttLog(sn string, cmdType string, dateStr string, data []domain.MqttLogItem) (objectName string, err error) { err = nil f := excelize.NewFile() defer func() { closeErr := f.Close() if closeErr != nil { zap.L().Error("Excel 文件close失败", zap.String("err", err.Error())) if err == nil { err = closeErr } } }() // 写标题行 f.SetCellValue("Sheet1", "A1", "Type") f.SetCellValue("Sheet1", "B1", "Time") f.SetCellValue("Sheet1", "C1", "Data") // 写入数据 for i, d := range data { f.SetCellValue("Sheet1", "A"+strconv.Itoa(i+2), d.CmdType) f.SetCellValue("Sheet1", "B"+strconv.Itoa(i+2), d.LogTime) f.SetCellValue("Sheet1", "C"+strconv.Itoa(i+2), d.LogData) } // 创建临时保存文件路径 tempPath, err := createTempPath("mqtt_" + cmdType) if err != nil { return } defer os.RemoveAll(tempPath) // 程序结束时清理临时目录 fileName := fmt.Sprintf("MqttLog_%s_%s.xlsx", cmdType, strings.ReplaceAll(dateStr, "-", "")) filePath := filepath.Join(tempPath, fileName) // 保存到文件 err = f.SaveAs(filePath) if err != nil { zap.L().Error("Excel文件save失败", zap.String("path", filePath), zap.String("err", err.Error())) return } // 上传到文件服务器 objectName = fmt.Sprintf("%s/%s/%s/%s", mainPathName, sn, subMqttPathName, fileName) if err = minio.UploadFile(filePath, objectName); err != nil { zap.L().Error("上传Excel文件失败", zap.String("path", filePath), zap.String("objectName", objectName), zap.String("err", err.Error())) return } return } // 判断EventAlarm excel文件是否存在 func ExistsBatchDownloadEventAlarm(sn string, dateStr string) (bool, error) { fileName := fmt.Sprintf("EventAlarm_%s.xlsx", strings.ReplaceAll(dateStr, "-", "")) objectName := fmt.Sprintf("%s/%s/%s/%s", mainPathName, sn, subEventAlarmPathName, fileName) exists, err := minio.ExistsObject(objectName) if err != nil { zap.L().Error("检查EventAlarm excel文件失败", zap.String("fileName", fileName), zap.String("objectName", objectName), zap.String("err", err.Error())) return false, err } return exists, nil } // 获取EventAlarm excel文件状态信息 func StatBatchDownloadEventAlarm(sn string, dateStr string) (*minio.ObjectInfo, error) { fileName := fmt.Sprintf("EventAlarm_%s.xlsx", strings.ReplaceAll(dateStr, "-", "")) objectName := fmt.Sprintf("%s/%s/%s/%s", mainPathName, sn, subEventAlarmPathName, fileName) status, err := minio.StatObject(objectName) if err != nil { zap.L().Error("获取EventAlarm excel文件状态失败", zap.String("fileName", fileName), zap.String("objectName", objectName), zap.String("err", err.Error())) return nil, err } return &status, nil } // 保存EventAlarm excel到文件服务器 // return objName func SaveBatchDownloadEventAlarm(sn string, dateStr string, eventAlarmData []domain.EventAlarmItem) (objectName string, err error) { err = nil f := excelize.NewFile() defer func() { closeErr := f.Close() if closeErr != nil { zap.L().Error("Excel 文件close失败", zap.String("err", err.Error())) if err == nil { err = closeErr } } }() // 写标题行 f.SetCellValue("Sheet1", "A1", "ID") f.SetCellValue("Sheet1", "B1", "Time") f.SetCellValue("Sheet1", "C1", "Type") f.SetCellValue("Sheet1", "D1", "AlarmCode") f.SetCellValue("Sheet1", "E1", "Name") f.SetCellValue("Sheet1", "F1", "StartTime") f.SetCellValue("Sheet1", "G1", "EndTime") f.SetCellValue("Sheet1", "H1", "Data") for i, data := range eventAlarmData { var id string if data.ID == nil || *data.ID == "" { id = strconv.Itoa(i + 1) } else { id = *data.ID } // EndTime 为 *string,事件或未结束的告警其值为 null,需先判 nil 再解引用, // 否则直接写入指针会被格式化为指针地址(如 0x1be7816885a0) endTime := "null" if data.EndTime != nil { endTime = *data.EndTime } f.SetCellValue("Sheet1", "A"+strconv.Itoa(i+2), id) f.SetCellValue("Sheet1", "B"+strconv.Itoa(i+2), data.LogTime) f.SetCellValue("Sheet1", "C"+strconv.Itoa(i+2), data.LogType) f.SetCellValue("Sheet1", "D"+strconv.Itoa(i+2), data.AlarmCode) f.SetCellValue("Sheet1", "E"+strconv.Itoa(i+2), data.LogName) f.SetCellValue("Sheet1", "F"+strconv.Itoa(i+2), data.StartTime) f.SetCellValue("Sheet1", "G"+strconv.Itoa(i+2), endTime) f.SetCellValue("Sheet1", "H"+strconv.Itoa(i+2), data.LogData) } // 创建临时保存文件路径 tempPath, err := createTempPath("eventAlarm") if err != nil { return } defer os.RemoveAll(tempPath) // 程序结束时清理临时目录 fileName := fmt.Sprintf("EventAlarm_%s.xlsx", strings.ReplaceAll(dateStr, "-", "")) filePath := filepath.Join(tempPath, fileName) // 保存到文件 err = f.SaveAs(filePath) if err != nil { zap.L().Error("Excel文件save失败", zap.String("path", filePath), zap.String("err", err.Error())) return } // 上传到文件服务器 objectName = fmt.Sprintf("%s/%s/%s/%s", mainPathName, sn, subEventAlarmPathName, fileName) if err = minio.UploadFile(filePath, objectName); err != nil { zap.L().Error("上传Excel文件失败", zap.String("path", filePath), zap.String("objectName", objectName), zap.String("err", err.Error())) return } return } // 判断AlarmSnapshot文件是否存在 func ExistsBatchDownloadAlarmSnapshot(sn string, dateStr string) (bool, error) { fileName := fmt.Sprintf("EventAlarm_%s.xlsx", strings.ReplaceAll(dateStr, "-", "")) objectName := fmt.Sprintf("%s/%s/%s/%s", mainPathName, sn, subAlarmSnapshotPathName, fileName) exists, err := minio.ExistsObject(objectName) if err != nil { zap.L().Error("检查AlarmSnapshot文件失败", zap.String("fileName", fileName), zap.String("objectName", objectName), zap.String("err", err.Error())) return false, err } return exists, nil } // 获取AlarmSnapshot文件状态信息 func StatBatchDownloadAlarmSnapshot(sn string, dateStr string) (*minio.ObjectInfo, error) { fileName := fmt.Sprintf("EventAlarm_%s.xlsx", strings.ReplaceAll(dateStr, "-", "")) objectName := fmt.Sprintf("%s/%s/%s/%s", mainPathName, sn, subAlarmSnapshotPathName, fileName) status, err := minio.StatObject(objectName) if err != nil { zap.L().Error("获取AlarmSnapshot文件状态失败", zap.String("fileName", fileName), zap.String("objectName", objectName), zap.String("err", err.Error())) return nil, err } return &status, nil } // 保存AlarmSnapshot文件到文件服务器 // return objName func SaveBatchDownloadAlarmSnapshot(sn string, dateStr string, alarmSnapshotData []*domain.SiteAlarmSnapshotData) (objectName string, err error) { err = nil f := excelize.NewFile() defer func() { closeErr := f.Close() if closeErr != nil { zap.L().Error("Excel 文件close失败", zap.String("err", err.Error())) if err == nil { err = closeErr } } }() // 写标题行 f.SetCellValue("Sheet1", "A1", "ID") f.SetCellValue("Sheet1", "B1", "Time") f.SetCellValue("Sheet1", "C1", "Data") // 数据按时间排序 sort.Slice(alarmSnapshotData[:], func(i, j int) bool { return alarmSnapshotData[i].LogTime.Before(alarmSnapshotData[j].LogTime) }) for i, data := range alarmSnapshotData { var logTime string loc, e := time.LoadLocation(data.ZoneInfo) if e != nil { zap.L().Warn("本地时间加载失败", zap.String("ZoneInfo", data.ZoneInfo), zap.String("err", err.Error())) logTime = data.LogTime.Format(time.DateTime) } else { logTime = data.LogTime.In(loc).Format(time.DateTime) } // 尝试解析成function. var snapJson function.SnapUnmarshalJson if err := json.Unmarshal([]byte(data.JSONData), &snapJson); err == nil { // 能解析成功,则为new json,需要转换 for _, xid := range snapJson.SnapInfo.XidList { if xid.Type == "VID" { macro, desc, err := repositories.GetEmsparaVidInfo(uint(xid.ID)) if err != nil { zap.L().Warn("GetEmsparaVidInfo error", zap.String("err", err.Error())) continue } xid.Macro = ¯o xid.Brief = &desc } else if xid.Type == "PID" { macro, desc, err := repositories.GetEmsparaPidInfo(uint(xid.ID)) if err != nil { zap.L().Warn("GetEmsparaPidInfo error", zap.String("err", err.Error())) continue } xid.Macro = ¯o xid.Brief = &desc } } if jsonBytes, err := json.Marshal(snapJson); err != nil { zap.L().Error("Marshal snapJson failed", zap.String("err", err.Error())) } else { data.JSONData = string(jsonBytes) } } f.SetCellValue("Sheet1", "A"+strconv.Itoa(i+2), data.ID) f.SetCellValue("Sheet1", "B"+strconv.Itoa(i+2), logTime) f.SetCellValue("Sheet1", "C"+strconv.Itoa(i+2), data.JSONData) } // 创建临时保存文件路径 tempPath, err := createTempPath("alarmSnapshot") if err != nil { return } defer os.RemoveAll(tempPath) // 程序结束时清理临时目录 fileName := fmt.Sprintf("EventAlarm_%s.xlsx", strings.ReplaceAll(dateStr, "-", "")) filePath := filepath.Join(tempPath, fileName) // 保存到文件 err = f.SaveAs(filePath) if err != nil { zap.L().Error("Excel文件save失败", zap.String("path", filePath), zap.String("err", err.Error())) return } // 上传到文件服务器 objectName = fmt.Sprintf("%s/%s/%s/%s", mainPathName, sn, subAlarmSnapshotPathName, fileName) if err = minio.UploadFile(filePath, objectName); err != nil { zap.L().Error("上传Excel文件失败", zap.String("path", filePath), zap.String("objectName", objectName), zap.String("err", err.Error())) return } return } // 判断EmsLog文件是否存在 func ExistsBatchDownloadEmsLog(sn string, originalFileName string, fileSupType string, dateStr string, callTime time.Time) (bool, error) { // 使用正则匹配多级后缀,例如 .tar.bz2, .tar.gz, .min.js 等 re := regexp.MustCompile(`(\.[a-zA-Z0-9]+)+$`) matchSuffix := re.FindString(originalFileName) fileName := fmt.Sprintf("EmsLog_%s_%s_%s_%s%s", sn, fileSupType, strings.ReplaceAll(dateStr, "-", "_"), strconv.FormatInt(callTime.Unix(), 10), matchSuffix) objectName := fmt.Sprintf("%s/%s/%s/%s", mainPathName, sn, subEmsLogsPathName, fileName) exists, err := minio.ExistsObject(objectName) if err != nil { zap.L().Error("检查EmsLog文件失败", zap.String("fileName", fileName), zap.String("objectName", objectName), zap.String("err", err.Error())) return false, err } return exists, nil } // 获取EmsLog文件状态信息 func StatBatchDownloadEmsLog(sn string, originalFileName string, fileSupType string, dateStr string, callTime time.Time) (*minio.ObjectInfo, error) { // 使用正则匹配多级后缀,例如 .tar.bz2, .tar.gz, .min.js 等 re := regexp.MustCompile(`(\.[a-zA-Z0-9]+)+$`) matchSuffix := re.FindString(originalFileName) fileName := fmt.Sprintf("EmsLog_%s_%s_%s_%s%s", sn, fileSupType, strings.ReplaceAll(dateStr, "-", "_"), strconv.FormatInt(callTime.Unix(), 10), matchSuffix) objectName := fmt.Sprintf("%s/%s/%s/%s", mainPathName, sn, subEmsLogsPathName, fileName) status, err := minio.StatObject(objectName) if err != nil { zap.L().Error("获取EmsLog文件状态失败", zap.String("fileName", fileName), zap.String("objectName", objectName), zap.String("err", err.Error())) return nil, err } return &status, nil } // 保存EmsLog文件到文件服务器 // return objName func SaveBatchDownloadEmsLog(sn string, dataBytes []byte, originalFileName string, fileSupType string, dateStr string, callTime time.Time) (string, error) { // 将 []byte 封装成 io.Reader dataReader := bytes.NewReader(dataBytes) // 使用正则匹配多级后缀,例如 .tar.bz2, .tar.gz, .min.js 等 re := regexp.MustCompile(`(\.[a-zA-Z0-9]+)+$`) matchSuffix := re.FindString(originalFileName) // 计算Content Type contentType := mimetype.GetContentType(originalFileName) // 保存到minio fileName := fmt.Sprintf("EmsLog_%s_%s_%s_%s%s", sn, fileSupType, strings.ReplaceAll(dateStr, "-", "_"), strconv.FormatInt(callTime.Unix(), 10), matchSuffix) objectName := fmt.Sprintf("%s/%s/%s/%s", mainPathName, sn, subEmsLogsPathName, fileName) return objectName, minio.UploadFromReader(dataReader, int64(len(dataBytes)), objectName, contentType) } // 判断FaultRecord文件是否存在 func ExistsBatchDownloadFaultRecord(sn string, faultRecordFileName string) (bool, error) { objectName := fmt.Sprintf("%s/%s/%s/%s", mainPathName, sn, subFaultRecordPathName, faultRecordFileName) exists, err := minio.ExistsObject(objectName) if err != nil { zap.L().Error("检查FaultRecord文件失败", zap.String("fileName", faultRecordFileName), zap.String("objectName", objectName), zap.String("err", err.Error())) return false, err } return exists, nil } // 获取FaultRecord文件状态信息 func StatBatchDownloadFaultRecord(sn string, faultRecordFileName string) (*minio.ObjectInfo, error) { objectName := fmt.Sprintf("%s/%s/%s/%s", mainPathName, sn, subFaultRecordPathName, faultRecordFileName) status, err := minio.StatObject(objectName) if err != nil { zap.L().Error("获取FaultRecord文件状态失败", zap.String("fileName", faultRecordFileName), zap.String("objectName", objectName), zap.String("err", err.Error())) return nil, err } return &status, nil } // 保存FaultRecord文件到文件服务器 // return objName func SaveBatchDownloadFaultRecord(sn string, dataBytes []byte, faultRecordFileName string) (string, error) { // 将 []byte 封装成 io.Reader dataReader := bytes.NewReader(dataBytes) // 计算Content Type contentType := mimetype.GetContentType(faultRecordFileName) // 保存到minio objectName := fmt.Sprintf("%s/%s/%s/%s", mainPathName, sn, subFaultRecordPathName, faultRecordFileName) return objectName, minio.UploadFromReader(dataReader, int64(len(dataBytes)), objectName, contentType) } // 判断UpdateLog excel文件是否存在 func ExistsBatchDownloadUpdateLog(sn string, date time.Time) (bool, error) { fileName := fmt.Sprintf("UpdateLog_%s_%s.xlsx", sn, strings.ReplaceAll(date.Format(time.DateTime), " ", "_")) objectName := fmt.Sprintf("%s/%s/%s/%s", mainPathName, sn, subUpdateLogPathName, fileName) exists, err := minio.ExistsObject(objectName) if err != nil { zap.L().Error("检查UpdateLog excel文件失败", zap.String("fileName", fileName), zap.String("objectName", objectName), zap.String("err", err.Error())) return false, err } return exists, nil } // 获取UpdateLog excel文件状态信息 func StatBatchDownloadUpdateLog(sn string, date time.Time) (*minio.ObjectInfo, error) { fileName := fmt.Sprintf("UpdateLog_%s_%s.xlsx", sn, strings.ReplaceAll(date.Format(time.DateTime), " ", "_")) objectName := fmt.Sprintf("%s/%s/%s/%s", mainPathName, sn, subUpdateLogPathName, fileName) status, err := minio.StatObject(objectName) if err != nil { zap.L().Error("获取UpdateLog excel文件状态失败", zap.String("fileName", fileName), zap.String("objectName", objectName), zap.String("err", err.Error())) return nil, err } return &status, nil } // 保存UpdateLog excel到文件服务器 // return objName func SaveBatchDownloadUpdateLog(sn string, systemUpdateLogData []domain.BatchDownloadSystemUpdateLogInfo, aHubUpdateLogData []domain.BatchDownloadAHubUpdateLogInfo) (objectName string, err error) { err = nil f := excelize.NewFile() defer func() { closeErr := f.Close() if closeErr != nil { zap.L().Error("Excel 文件close失败", zap.String("err", err.Error())) if err == nil { err = closeErr } } }() systemUpdateSheetName := "SystemUpdate" aHubUpdateSheetName := "aHubUpdate" err = f.SetSheetName("Sheet1", systemUpdateSheetName) if err != nil { zap.L().Error("Excel Sheet1更名失败", zap.String("sheetName", systemUpdateSheetName), zap.String("err", err.Error())) return } _, err = f.NewSheet(aHubUpdateSheetName) if err != nil { zap.L().Error("Excel 新增Sheet失败", zap.String("sheetName", aHubUpdateSheetName), zap.String("err", err.Error())) return } // 样式集 // 加粗 // 创建加粗样式 styleBold, err := f.NewStyle(&excelize.Style{ Font: &excelize.Font{ Bold: true, }, }) if err != nil { zap.L().Error("Excel 新增Sheet失败", zap.String("err", err.Error())) return } var rowIndex int = 1 // 写入系统升级日志 for _, updateItem := range systemUpdateLogData { // 写标题行 f.SetCellValue(systemUpdateSheetName, fmt.Sprintf("A%d", rowIndex), "OrderNo") f.SetCellValue(systemUpdateSheetName, fmt.Sprintf("B%d", rowIndex), "GateSn") f.SetCellValue(systemUpdateSheetName, fmt.Sprintf("C%d", rowIndex), "TriggerType") f.SetCellValue(systemUpdateSheetName, fmt.Sprintf("D%d", rowIndex), "FileName") f.SetCellValue(systemUpdateSheetName, fmt.Sprintf("E%d", rowIndex), "UpdateTime") f.SetCellValue(systemUpdateSheetName, fmt.Sprintf("F%d", rowIndex), "Result") f.SetCellStyle(systemUpdateSheetName, fmt.Sprintf("A%d", rowIndex), fmt.Sprintf("F%d", rowIndex), styleBold) rowIndex++ // 写入项目信息内容 f.SetCellValue(systemUpdateSheetName, fmt.Sprintf("A%d", rowIndex), updateItem.OrderNo) f.SetCellValue(systemUpdateSheetName, fmt.Sprintf("B%d", rowIndex), updateItem.GateSn) f.SetCellValue(systemUpdateSheetName, fmt.Sprintf("C%d", rowIndex), updateItem.TriggerType) f.SetCellValue(systemUpdateSheetName, fmt.Sprintf("D%d", rowIndex), updateItem.FileName) f.SetCellValue(systemUpdateSheetName, fmt.Sprintf("E%d", rowIndex), updateItem.UpdateTime) f.SetCellValue(systemUpdateSheetName, fmt.Sprintf("F%d", rowIndex), updateItem.Result) rowIndex = rowIndex + 2 // Gate版本信息 f.SetCellValue(systemUpdateSheetName, fmt.Sprintf("A%d", rowIndex), "GateVer") f.SetCellStyle(systemUpdateSheetName, fmt.Sprintf("A%d", rowIndex), fmt.Sprintf("A%d", rowIndex), styleBold) rowIndex++ for idx, modeVer := range updateItem.GateVerList { excelSetCellValue(f, systemUpdateSheetName, (3*idx)+1, rowIndex, "Name") excelSetCellValue(f, systemUpdateSheetName, (3*idx)+1, rowIndex+1, "OldVer") excelSetCellValue(f, systemUpdateSheetName, (3*idx)+1, rowIndex+2, "NewVer") excelSetCellValue(f, systemUpdateSheetName, (3*idx)+2, rowIndex, modeVer.Name) excelSetCellValue(f, systemUpdateSheetName, (3*idx)+2, rowIndex+1, modeVer.OldVer) excelSetCellValue(f, systemUpdateSheetName, (3*idx)+2, rowIndex+2, modeVer.NewVer) } rowIndex = rowIndex + 2 // SubDev版本信息 f.SetCellValue(systemUpdateSheetName, fmt.Sprintf("A%d", rowIndex), "SubVer") f.SetCellStyle(systemUpdateSheetName, fmt.Sprintf("A%d", rowIndex), fmt.Sprintf("A%d", rowIndex), styleBold) rowIndex++ for _, subVer := range updateItem.SubDevList { f.SetCellValue(systemUpdateSheetName, fmt.Sprintf("A%d", rowIndex), "SubSn") f.SetCellValue(systemUpdateSheetName, fmt.Sprintf("B%d", rowIndex), subVer.DevSn) rowIndex++ for idx, modeVer := range subVer.DevVerList { excelSetCellValue(f, systemUpdateSheetName, (3*idx)+1, rowIndex, "Name") excelSetCellValue(f, systemUpdateSheetName, (3*idx)+1, rowIndex+1, "OldVer") excelSetCellValue(f, systemUpdateSheetName, (3*idx)+1, rowIndex+2, "NewVer") excelSetCellValue(f, systemUpdateSheetName, (3*idx)+2, rowIndex, modeVer.Name) excelSetCellValue(f, systemUpdateSheetName, (3*idx)+2, rowIndex+1, modeVer.OldVer) excelSetCellValue(f, systemUpdateSheetName, (3*idx)+2, rowIndex+2, modeVer.NewVer) } rowIndex = rowIndex + 3 } rowIndex++ } rowIndex = 1 // 写入aHub升级日志 for _, aHubUpdateItem := range aHubUpdateLogData { // 写标题行 f.SetCellValue(aHubUpdateSheetName, fmt.Sprintf("A%d", rowIndex), "OrderNo") f.SetCellValue(aHubUpdateSheetName, fmt.Sprintf("B%d", rowIndex), "GateSn") f.SetCellValue(aHubUpdateSheetName, fmt.Sprintf("C%d", rowIndex), "TriggerType") f.SetCellValue(aHubUpdateSheetName, fmt.Sprintf("D%d", rowIndex), "AHubSn") f.SetCellValue(aHubUpdateSheetName, fmt.Sprintf("E%d", rowIndex), "EndTime") f.SetCellValue(aHubUpdateSheetName, fmt.Sprintf("F%d", rowIndex), "Result") f.SetCellStyle(aHubUpdateSheetName, fmt.Sprintf("A%d", rowIndex), fmt.Sprintf("F%d", rowIndex), styleBold) rowIndex++ // 写入项目信息内容 f.SetCellValue(aHubUpdateSheetName, fmt.Sprintf("A%d", rowIndex), aHubUpdateItem.OrderNo) f.SetCellValue(aHubUpdateSheetName, fmt.Sprintf("B%d", rowIndex), aHubUpdateItem.GateSn) f.SetCellValue(aHubUpdateSheetName, fmt.Sprintf("C%d", rowIndex), aHubUpdateItem.TriggerType) f.SetCellValue(aHubUpdateSheetName, fmt.Sprintf("D%d", rowIndex), aHubUpdateItem.AHubSn) f.SetCellValue(aHubUpdateSheetName, fmt.Sprintf("E%d", rowIndex), aHubUpdateItem.EndTime) f.SetCellValue(aHubUpdateSheetName, fmt.Sprintf("F%d", rowIndex), aHubUpdateItem.Result) rowIndex = rowIndex + 2 // aHub版本信息 f.SetCellValue(aHubUpdateSheetName, fmt.Sprintf("A%d", rowIndex), "aHubVer") f.SetCellStyle(aHubUpdateSheetName, fmt.Sprintf("A%d", rowIndex), fmt.Sprintf("A%d", rowIndex), styleBold) rowIndex++ for idx, modeVer := range aHubUpdateItem.DetailList { excelSetCellValue(f, aHubUpdateSheetName, (3*idx)+1, rowIndex, "FileName") excelSetCellValue(f, aHubUpdateSheetName, (3*idx)+1, rowIndex+1, "FileOrder") excelSetCellValue(f, aHubUpdateSheetName, (3*idx)+1, rowIndex+2, "ResultDesc") excelSetCellValue(f, aHubUpdateSheetName, (3*idx)+1, rowIndex+3, "OldVer") excelSetCellValue(f, aHubUpdateSheetName, (3*idx)+1, rowIndex+4, "NewVer") excelSetCellValue(f, aHubUpdateSheetName, (3*idx)+2, rowIndex, modeVer.FileName) excelSetCellValue(f, aHubUpdateSheetName, (3*idx)+2, rowIndex+1, modeVer.FileOrder) excelSetCellValue(f, aHubUpdateSheetName, (3*idx)+2, rowIndex+2, modeVer.ResultDesc) excelSetCellValue(f, aHubUpdateSheetName, (3*idx)+2, rowIndex+3, modeVer.OldVer) excelSetCellValue(f, aHubUpdateSheetName, (3*idx)+2, rowIndex+4, modeVer.NewVer) } rowIndex = rowIndex + 6 } // 创建临时保存文件路径 tempPath, err := createTempPath("updateLog") if err != nil { return } defer os.RemoveAll(tempPath) // 程序结束时清理临时目录 fileName := fmt.Sprintf("UpdateLog_%s_%s.xlsx", sn, strings.ReplaceAll(time.Now().Format(time.DateTime), " ", "_")) filePath := filepath.Join(tempPath, fileName) // 保存到文件 err = f.SaveAs(filePath) if err != nil { zap.L().Error("Excel文件save失败", zap.String("path", filePath), zap.String("err", err.Error())) return } // 上传到文件服务器 objectName = fmt.Sprintf("%s/%s/%s/%s", mainPathName, sn, subUpdateLogPathName, fileName) if err = minio.UploadFile(filePath, objectName); err != nil { zap.L().Error("上传Excel文件失败", zap.String("path", filePath), zap.String("objectName", objectName), zap.String("err", err.Error())) return } return } // 保存Settings excel到文件服务器 // return objName // func SaveBatchDownloadSettings(sn string, dateStr string, settingsData []domain.SiteSettings) (string, error) { // }