package outgiving import ( "bytes" "fmt" "git.inspur.com/sbg-jszt/cfn/cfn-schedule/internal/controller" "git.inspur.com/sbg-jszt/cfn/cfn-schedule/internal/controller/v1/app_manage" "git.inspur.com/sbg-jszt/cfn/cfn-schedule/internal/middleware" "git.inspur.com/sbg-jszt/cfn/cfn-schedule/internal/model/app_manage/node" "git.inspur.com/sbg-jszt/cfn/cfn-schedule/internal/model/app_manage/out_giving" "git.inspur.com/sbg-jszt/cfn/cfn-schedule/internal/model/app_manage/out_giving/status" "git.inspur.com/sbg-jszt/cfn/cfn-schedule/internal/model/app_manage/project_info" "git.inspur.com/sbg-jszt/cfn/cfn-schedule/internal/model/user" "git.inspur.com/sbg-jszt/cfn/cfn-schedule/internal/pkg/file_download_client" "git.inspur.com/sbg-jszt/cfn/cfn-schedule/internal/pkg/request" "git.inspur.com/sbg-jszt/cfn/cfn-schedule/internal/pkg/response" "git.inspur.com/sbg-jszt/cfn/cfn-schedule/internal/pkg/secure" "git.inspur.com/sbg-jszt/cfn/cfn-schedule/internal/pkg/utils" "git.inspur.com/sbg-jszt/cfn/cfn-schedule/internal/pkg/utils/file_utils" "git.inspur.com/sbg-jszt/cfn/cfn-schedule/internal/service/app_manage/outgiving" "git.inspur.com/sbg-jszt/cfn/cfn-schedule/pkg/log" "github.com/gin-gonic/gin" "github.com/google/uuid" "io/ioutil" "net/http" "path/filepath" "strings" "time" ) // 分发文件管理 func GetItemData(c *gin.Context) { workspaceId := app_manage.GetWorkspaceId(c) bodyToMap := request.GetBodyToMap(c) id, ok := bodyToMap["id"].(string) if !ok { controller.Fail(c, http.StatusBadRequest, "参数错误:id") return } outGivingModel := out_giving.GetOutGivingModelById(id) if outGivingModel.Id == "" { controller.Fail(c, http.StatusBadRequest, "没有数据") return } outGivingNodeProjectList := outGivingModel.GetOutGivingNodeProjectList() nodeMap := mapNodeModels(outGivingNodeProjectList) projectMap := mapProjectInfoCacheModels(outGivingNodeProjectList, workspaceId) collect := transformOutGivingNodeProjects(outGivingNodeProjectList, workspaceId, nodeMap, projectMap, id) data := map[string]interface{}{ "data": outGivingModel, "projectList": collect, } controller.Success(c, data) } func mapNodeModels(outGivingNodeProjectList []out_giving.OutGivingNodeProject) map[string]node.Node { var nodeIds []string for _, project := range outGivingNodeProjectList { nodeIds = append(nodeIds, project.NodeId) } nodeModels := node.GetNodeByIds(nodeIds) nodeMap := make(map[string]node.Node) for _, model := range nodeModels { nodeMap[model.Id] = model } return nodeMap } func mapProjectInfoCacheModels(outGivingNodeProjectList []out_giving.OutGivingNodeProject, workspaceId string) map[string]project_info.ProjectInfo { var ids []string for _, project := range outGivingNodeProjectList { projectId := secure.Sha1Encrypt(workspaceId + project.NodeId + project.ProjectId) // todo 检查projectId是否能对得上 ids = append(ids, projectId) } var projectInfoCacheModels = project_info.GetProjectByIds(ids) projectMap := make(map[string]project_info.ProjectInfo) for _, model := range projectInfoCacheModels { projectMap[model.Id] = model } return projectMap } func transformOutGivingNodeProjects(outGivingNodeProjectList []out_giving.OutGivingNodeProject, workspaceId string, nodeMap map[string]node.Node, projectMap map[string]project_info.ProjectInfo, id string) []map[string]interface{} { var collect []map[string]interface{} for _, outGivingNodeProject := range outGivingNodeProjectList { nodeModel, exists := nodeMap[outGivingNodeProject.NodeId] if !exists { continue } // 填充数据ID,项目数据是先保存至逻辑节点后又同步的思路,所以NodeScriptCacheModel在逻辑节点的Id就是ProjectId,在服务端保存时,将Id保存至ProjectId字段, // 然后根据secure.Sha1Encrypt(item.WorkspaceId + item.NodeId + item.ProjectId)生成Id fullId := secure.Sha1Encrypt(workspaceId + outGivingNodeProject.NodeId + outGivingNodeProject.ProjectId) projectInfoCacheModel, exists := projectMap[fullId] var cacheProjectName interface{} if exists { cacheProjectName = projectInfoCacheModel.Name } outGivingLog, _ := outgiving.GetByProject(id, outGivingNodeProject) var outGivingStatus, outGivingResult, lastTime, fileSize, progressSize interface{} if outGivingLog != nil { outGivingStatus = outGivingLog.Status outGivingResult = outGivingLog.Result lastTime = outGivingLog.CreateTimeMillis fileSize = outGivingLog.FileSize progressSize = outGivingLog.ProgressSize } jsonObject := map[string]interface{}{ "sortValue": outGivingNodeProject.SortValue, "disabled": outGivingNodeProject.Disabled, "nodeId": outGivingNodeProject.NodeId, "projectId": outGivingNodeProject.ProjectId, "nodeName": nodeModel.Name, "id": fullId, "cacheProjectName": cacheProjectName, "outGivingStatus": outGivingStatus, "outGivingResult": outGivingResult, "lastTime": lastTime, "fileSize": fileSize, "progressSize": progressSize, } collect = append(collect, jsonObject) } return collect } func UploadSharding(c *gin.Context) { // 获取FormData form, err := c.MultipartForm() if err != nil { controller.FailCode(c, http.StatusInternalServerError, err, "解析请求参数失败") return } id := form.Value["id"][0] // 状态判断 _, err = check(id, func(statusCode int, o *out_giving.OutGivingModel) error { if statusCode == status.ING { return fmt.Errorf("当前还在分发中,请等待分发结束") } return nil }) if err != nil { controller.FailCode(c, http.StatusInternalServerError, err, "当前还在分发中,请等待分发结束") return } // 提取表单字段 nowSlice := form.Value["nowSlice"][0] totalSlice := form.Value["totalSlice"][0] sliceID := form.Value["sliceId"][0] fileSumMD5 := form.Value["fileSumMd5"][0] // 获取上传的文件 file, header, err := c.Request.FormFile("file") if err != nil { controller.FailCode(c, http.StatusInternalServerError, err, "Failed to get file") return } defer file.Close() userObject, _ := c.Get(middleware.LoginUserKey) userModel := userObject.(*user.UserObj) tempPathName, err := utils.GetUserTempPath(userModel.UserID) if err != nil { controller.FailCode(c, http.StatusInternalServerError, err, "上传失败:创建临时目录失败") return } err = app_manage.UploadShardingImpl(file, header.Filename, tempPathName, sliceID, totalSlice, nowSlice, fileSumMD5, nil) if err != nil { controller.FailCode(c, http.StatusInternalServerError, err, err.Error()) return } controller.Success(c, "上传成功") } func check(id string, consumer func(int, *out_giving.OutGivingModel) error) (*out_giving.OutGivingModel, error) { outGivingModel := out_giving.GetOutGivingModelById(id) if outGivingModel.Id == "" { return nil, fmt.Errorf("上传失败,没有找到对应的分发项目: %s", id) } // 检查状态 statusCode := outGivingModel.Status return outGivingModel, consumer(statusCode, outGivingModel) } func ShardingMerge(c *gin.Context) { var ( autoUnzip bool secondaryDirectory string stripComponents float64 sliceID string totalSlice float64 fileSumMD5 string afterOpt float64 clearOld bool id string selectProject string ) bodyToMap := request.GetBodyToMap(c) autoUnzip, ok := bodyToMap["autoUnzip"].(bool) // type secondaryDirectory, ok = bodyToMap["secondaryDirectory"].(string) if !ok { controller.Fail(c, http.StatusBadRequest, "参数错误:levelName") return } stripComponents, ok = bodyToMap["stripComponents"].(float64) sliceID, ok = bodyToMap["sliceId"].(string) if !ok { controller.Fail(c, http.StatusBadRequest, "参数错误:sliceID") return } totalSlice, ok = bodyToMap["totalSlice"].(float64) if !ok { controller.Fail(c, http.StatusBadRequest, "参数错误:totalSlice") return } fileSumMD5, ok = bodyToMap["fileSumMd5"].(string) if !ok { controller.Fail(c, http.StatusBadRequest, "参数错误:fileSumMD5") return } afterOpt, ok = bodyToMap["afterOpt"].(float64) id, ok = bodyToMap["id"].(string) if !ok { controller.Fail(c, http.StatusBadRequest, "参数错误:id") return } clearOld, ok = bodyToMap["clearOld"].(bool) selectProject, ok = bodyToMap["selectProject"].(string) // 状态判断 _, err := check(id, func(statusCode int, o *out_giving.OutGivingModel) error { if statusCode == status.ING { return fmt.Errorf("当前还在分发中,请等待分发结束") } return nil }) if err != nil { controller.FailCode(c, http.StatusInternalServerError, err, "当前还在分发中,请等待分发结束") return } userObject, _ := c.Get(middleware.LoginUserKey) userModel := userObject.(*user.UserObj) tempPathName, err := utils.GetUserTempPath(userModel.UserID) if err != nil { controller.FailCode(c, http.StatusInternalServerError, err, "上传失败:创建临时目录失败[tempPathName]") return } path, err := utils.GetDataPath() if err != nil { controller.FailCode(c, http.StatusInternalServerError, err, "上传失败:创建临时目录失败[data]") return } destDir := filepath.Join(path, "outGiving", id) err = file_utils.MkdirAll(destDir) if err != nil { controller.FailCode(c, http.StatusInternalServerError, err, "上传失败:创建临时目录失败[outGiving Dir]") return } successFile, err := app_manage.ShardingTryMergeImpl(tempPathName, sliceID, int(totalSlice), fileSumMD5) if err != nil { controller.FailCode(c, http.StatusInternalServerError, err, err.Error()) return } // 从绝对路径successFile.Name中获取文件名 successFileName := filepath.Base(successFile.Name()) destFile := filepath.Join(destDir, successFileName) destFile, err = checkZip(destFile, autoUnzip) if err != nil { controller.FailCode(c, http.StatusInternalServerError, err, err.Error()) return } err = file_utils.MoveFileOrDir(successFile.Name(), destDir, true) if err != nil { controller.FailCode(c, http.StatusInternalServerError, err, "上传失败:创建临时目录失败[outGiving dest]") return } outGivingModel := out_giving.NewOutGivingModel() outGivingModel.Id = id outGivingModel.ClearOld = clearOld after := int(afterOpt) outGivingModel.AfterOpt = after outGivingModel.SecondaryDirectory = &secondaryDirectory outGivingModel.ModifyUser = userModel.UserID outGivingModel.ModifyTimeMillis = time.Now().UnixMilli() out_giving.UpdateOutGivingModel(outGivingModel, []string{"after_opt", "secondary_directory", "clear_old", "secondary_directory", "modify_user", "modify_time_millis"}) stripComponentsInt := int(stripComponents) outGivingExecId := uuid.New().String() outGivingRun := outgiving.NewOutGivingRun(id, destFile, userModel, autoUnzip, stripComponentsInt, true) go outGivingRun.StartRun(selectProject, outGivingExecId) //outGivingRun.StartRun(selectProject) dataResp := response.Resp() dataResp.SetCode(http.StatusOK) dataResp.Result.Data = map[string]string{ "result": "上传成功,开始分发!", "outGivingExecId": outGivingExecId, } dataResp.Success(c) //controller.Success(c, "上传成功,开始分发!") } func RemoteDownload(c *gin.Context) { var ( id string afterOpt float64 clearOld bool url string autoUnzip bool secondaryDirectory string stripComponents float64 selectProject string filename string ) bodyToMap := request.GetBodyToMap(c) id, ok := bodyToMap["id"].(string) if !ok { controller.Fail(c, http.StatusBadRequest, "参数错误:id") return } afterOpt, ok = bodyToMap["afterOpt"].(float64) clearOld, ok = bodyToMap["clearOld"].(bool) url, ok = bodyToMap["url"].(string) if !ok { controller.Fail(c, http.StatusBadRequest, "参数错误:url") return } autoUnzip, ok = bodyToMap["autoUnzip"].(bool) // type secondaryDirectory, ok = bodyToMap["secondaryDirectory"].(string) if !ok { controller.Fail(c, http.StatusBadRequest, "参数错误:secondaryDirectory") return } stripComponents, ok = bodyToMap["stripComponents"].(float64) selectProject, ok = bodyToMap["selectProject"].(string) filename, ok = bodyToMap["filename"].(string) // 状态判断 outGivingModel, err := check(id, func(statusCode int, o *out_giving.OutGivingModel) error { if statusCode == status.ING { return fmt.Errorf("当前还在分发中,请等待分发结束") } return nil }) if err != nil { controller.FailCode(c, http.StatusInternalServerError, err, "当前还在分发中,请等待分发结束") return } userObject, _ := c.Get(middleware.LoginUserKey) userModel := userObject.(*user.UserObj) outGivingModel.ClearOld = clearOld after := afterOpt outGivingModel.AfterOpt = int(after) outGivingModel.SecondaryDirectory = &secondaryDirectory outGivingModel.ModifyUser = userModel.UserID outGivingModel.ModifyTimeMillis = time.Now().UnixMilli() out_giving.UpdateOutGivingModel(outGivingModel, []string{"after_opt", "secondary_directory", "clear_old", "secondary_directory", "modify_user", "modify_time_millis"}) // 下载 path, err := utils.GetDataPath() if err != nil { controller.FailCode(c, http.StatusInternalServerError, err, "上传失败:创建临时目录失败[data]") return } destDir := filepath.Join(path, "outGiving", id) err = file_utils.MkdirAll(destDir) if err != nil { controller.FailCode(c, http.StatusInternalServerError, err, "上传失败:创建临时目录失败[outGiving Dir]") return } var destFile string if len(filename) == 0 { destFile = filepath.Join(destDir, uuid.New().String()) } else { destFile = filepath.Join(destDir, filename) } err = file_download_client.DownloadFileToLocal(url, destFile, 2) if err != nil { controller.FailCode(c, http.StatusInternalServerError, err, "下载文件失败") return } destFile, err = CheckType(destFile, autoUnzip) if err != nil { controller.FailCode(c, http.StatusInternalServerError, err, err.Error()) return } // 开启 stripComponentsInt := int(stripComponents) outGivingExecId := uuid.New().String() outGivingRun := outgiving.NewOutGivingRun(id, destFile, userModel, autoUnzip, stripComponentsInt, true) go outGivingRun.StartRun(selectProject, outGivingExecId) dataResp := response.Resp() dataResp.SetCode(http.StatusOK) dataResp.Result.Data = map[string]string{ "result": "上传成功,开始分发!", "outGivingExecId": outGivingExecId, } dataResp.Success(c) //controller.Success(c, "上传成功,开始分发!") } func CheckType(filePath string, unzip bool) (string, error) { if !unzip { return filePath, nil } data, err := ioutil.ReadFile(filePath) if err != nil { log.Errorf("Error reading file: %v", err) return "", fmt.Errorf("读取文件失败: %s", filePath) } switch { case bytes.HasPrefix(data, []byte{0x50, 0x4b, 0x03, 0x04}): // ZIP format return filePath, nil case bytes.HasPrefix(data, []byte{0x1f, 0x8b}): return filePath, nil default: log.Errorf("UnSupported file type.") return "", fmt.Errorf("不支持的文件类型: %s", filePath) } } var PACKAGE_EXT = []string{"tar.gz", "zip"} // 检查并处理ZIP文件 func checkZip(path string, unzip bool) (string, error) { if unzip { zip := false for _, ext := range PACKAGE_EXT { if strings.HasSuffix(strings.ToLower(path), strings.ToLower(ext)) { zip = true break } } if !zip { return "", fmt.Errorf("不支持的文件类型: %s", filepath.Base(path)) } } // 这里仅返回文件对象,未执行解压逻辑,如需解压还需额外实现 return path, nil } func Cancel(c *gin.Context) { bodyToMap := request.GetBodyToMap(c) id, ok := bodyToMap["id"].(string) if !ok { controller.Fail(c, http.StatusBadRequest, "参数错误:id") return } // 状态判断 outGigving, err := check(id, func(statusCode int, o *out_giving.OutGivingModel) error { if statusCode != status.ING { return fmt.Errorf("当前状态不是分发中") } return nil }) if err != nil { controller.FailCode(c, http.StatusInternalServerError, err, "当前状态不是分发中") return } userObject, _ := c.Get(middleware.LoginUserKey) userModel := userObject.(*user.UserObj) outgiving.Cancel(outGigving.Id, userModel) controller.Success(c, "取消成功") }