package service import ( "crypto/md5" "ego/internal/model" "ego/internal/serializer" "ego/internal/types" "ego/pkg/logger" "fmt" "io" "os" "path/filepath" "strings" "time" "github.com/gin-gonic/gin" "gorm.io/gorm" ) // SysDeployFileService 部署文件服务 type SysDeployFileService struct { Db *gorm.DB } // NewSysDeployFileService 构建部署文件服务 func NewSysDeployFileService(db *gorm.DB) *SysDeployFileService { return &SysDeployFileService{ Db: db, } } // Create 创建部署文件记录 func (s *SysDeployFileService) Create(c *gin.Context) serializer.Response { // 获取上传的文件 file, err := c.FormFile("file") if err != nil { logger.Error(c, "获取上传文件失败!") return serializer.ParamErr("获取上传文件失败!", err) } // 校验文件类型 if !strings.HasSuffix(strings.ToLower(file.Filename), ".zip") { logger.Error(c, "只支持zip格式文件!") return serializer.ParamErr("只支持zip格式文件!", nil) } // 获取文件名(不包含扩展名) filename := strings.TrimSuffix(file.Filename, filepath.Ext(file.Filename)) var deployFile model.SysDeployFile deployFile.FileName = filename // 生成文件ID if id, err := SysSequenceServiceBuilder(deployFile.TableName()).GenerateId(); err == nil { deployFile.FileId = id } else { return serializer.DBErr("序列生成失败!", err) } // 生成新的文件名:使用文件ID + .zip 后缀 newFileName := deployFile.FileId + ".zip" // 设置文件保存路径 deployFile.FilePath = filepath.Join("/data", newFileName) // 保存文件到指定路径 if err := c.SaveUploadedFile(file, deployFile.FilePath); err != nil { logger.Error(c, "保存文件失败!") return serializer.ParamErr("保存文件失败!", err) } // 打开保存的文件以计算哈希值 savedFile, err := os.Open(deployFile.FilePath) if err != nil { logger.Error(c, "打开保存的文件失败!") return serializer.ParamErr("打开保存的文件失败!", err) } defer savedFile.Close() // 计算文件哈希值 deployFile.FileHash, err = s.CalculateFileHash(savedFile) if err != nil { logger.Error(c, "计算文件哈希值失败!") return serializer.ParamErr("计算文件哈希值失败!", err) } // 获取文件大小(从保存的文件获取更准确) fileInfo, err := savedFile.Stat() if err != nil { logger.Error(c, "获取文件信息失败!") return serializer.ParamErr("获取文件信息失败!", err) } deployFile.FileSize = fileInfo.Size() // 设置默认值 now := time.Now() deployFile.CreateTime = &now deployFile.Status = model.FileStatusNotUsed deployFile.DelFlag = "0" // 获取当前用户 if createBy := c.GetString("id"); createBy != "" { deployFile.CreateBy = createBy } if err := s.Db.Create(&deployFile).Error; err != nil { logger.Error(c, "创建部署文件记录失败!") return serializer.DBErr("创建部署文件记录失败!", err) } return serializer.Succ("创建部署文件记录成功!", deployFile) } // GetByID 根据ID获取部署文件记录 func (s *SysDeployFileService) GetByID(c *gin.Context) serializer.Response { // 获取当前用户ID currentUserId := c.GetString("id") if currentUserId == "" { return serializer.ParamErr("用户信息获取失败!", nil) } var deployFile model.SysDeployFile if err := s.Db.Where("file_id = ? AND del_flag = ? AND create_by = ?", c.Param("id"), "0", currentUserId).First(&deployFile).Error; err != nil { logger.Error(c, "获取部署文件记录失败!") return serializer.DBErr("获取部署文件记录失败!", err) } return serializer.Succ("查询成功!", deployFile) } // UpdateByID 根据ID更新部署文件记录 func (s *SysDeployFileService) UpdateByID(c *gin.Context) serializer.Response { var deployFile model.SysDeployFile if err := c.ShouldBind(&deployFile); err != nil { logger.Error(c, "参数绑定失败!") return serializer.ParamErr("参数绑定失败!", err) } id := deployFile.FileId if id == "" { logger.Error(c, "id 不可为空!") return serializer.ParamErr("id不可为空!", fmt.Errorf("id不可为空")) } if deployFile.ParentId == "" { logger.Error(c, "项目ID不能为空!") return serializer.ParamErr("项目ID不能为空!", fmt.Errorf("项目ID不能为空")) } // 获取当前用户ID currentUserId := c.GetString("id") if currentUserId == "" { return serializer.ParamErr("用户信息获取失败!", nil) } // 检查权限:只能更新自己创建的数据 var existingDeployFile model.SysDeployFile if err := s.Db.Where("file_id = ? AND del_flag = ? AND create_by = ?", id, "0", currentUserId).First(&existingDeployFile).Error; err != nil { logger.Error(c, "部署文件记录不存在或无权限访问!") return serializer.ParamErr("部署文件记录不存在或无权限访问!", err) } // 设置更新时间 now := time.Now() deployFile.UpdateTime = &now // 获取当前用户 deployFile.UpdateBy = currentUserId if err := s.Db.Model(&deployFile).Where("file_id = ? AND del_flag = ? AND create_by = ?", id, "0", currentUserId).Updates(&deployFile).Error; err != nil { logger.Error(c, "更新部署文件记录失败!") return serializer.DBErr("更新部署文件记录失败!", err) } return serializer.Succ("更新部署文件记录成功!", deployFile) } // DeleteByID 根据ID删除部署文件记录 func (s *SysDeployFileService) DeleteByID(c *gin.Context) serializer.Response { id := c.Param("id") if id == "" { logger.Error(c, "id 不可为空!") return serializer.ParamErr("id不可为空!", fmt.Errorf("id不可为空")) } // 获取当前用户ID currentUserId := c.GetString("id") if currentUserId == "" { return serializer.ParamErr("用户信息获取失败!", nil) } // 软删除 data := map[string]any{ "del_flag": "1", "update_time": time.Now(), "update_by": currentUserId, } // 删除数据库记录 if err := s.Db.Model(&model.SysDeployFile{}).Where("file_id = ? AND create_by = ?", id, currentUserId).Updates(data).Error; err != nil { logger.Error(c, "删除部署文件记录失败!") return serializer.DBErr("删除部署文件记录失败!", err) } return serializer.Succ("删除部署文件记录成功!", nil) } // GetByCondition 条件查询部署文件记录 func (s *SysDeployFileService) GetByCondition(c *gin.Context) serializer.Response { var p types.Params if err := c.ShouldBind(&p); err != nil { return serializer.ParamErr("参数绑定失败!", err) } queryStr, args, err := p.ConvertToGormConditions() if err != nil { logger.Error(c, "参数绑定失败!") return serializer.ParamErr("参数绑定失败!", err) } var total int64 var deployFiles []model.SysDeployFile offset := (p.Page - 1) * p.Limit // 获取当前用户ID currentUserId := c.GetString("id") if currentUserId == "" { return serializer.ParamErr("用户信息获取失败!", nil) } // 构建基础查询 db := s.Db.Model(&model.SysDeployFile{}) // 如果有查询条件,添加条件 if queryStr != "" { db = db.Where(queryStr, args...) } // 添加用户权限过滤:只能查询自己创建的数据 db = db.Where("create_by = ?", currentUserId) // 排序 if p.Sort != "" { db = db.Order(p.Sort) } else { db = db.Order("create_time DESC") } // 执行分页查询 if err := db.Where("del_flag = ?", "0").Offset(offset).Limit(p.Limit).Find(&deployFiles).Error; err != nil { logger.Error(c, "获取部署文件记录失败!") return serializer.DBErr("获取部署文件记录失败!", err) } // 执行总数查询 if err := db.Where("del_flag = ?", "0").Count(&total).Error; err != nil { logger.Error(c, "获取部署文件记录总数失败!") return serializer.DBErr("获取部署文件记录总数失败!", err) } return serializer.Succ("查询成功!", gin.H{ "total": total, "items": deployFiles, "page": p.Page, "limit": p.Limit, }) } // GetByParentID 根据项目ID获取文件列表 func (s *SysDeployFileService) GetByParentID(c *gin.Context) serializer.Response { parentId := c.Param("parentId") if parentId == "" { logger.Error(c, "项目ID不可为空!") return serializer.ParamErr("项目ID不可为空!", fmt.Errorf("项目ID不可为空")) } // 获取当前用户ID currentUserId := c.GetString("id") if currentUserId == "" { return serializer.ParamErr("用户信息获取失败!", nil) } var deployFiles []model.SysDeployFile if err := s.Db.Where("parent_id = ? AND del_flag = ? AND create_by = ?", parentId, "0", currentUserId). Order("create_time DESC").Find(&deployFiles).Error; err != nil { logger.Error(c, "获取文件列表失败!") return serializer.DBErr("获取文件列表失败!", err) } return serializer.Succ("查询成功!", deployFiles) } // SetActiveFile 设置活跃文件 func (s *SysDeployFileService) SetActiveFile(c *gin.Context) serializer.Response { fileId := c.Param("id") if fileId == "" { logger.Error(c, "文件ID不可为空!") return serializer.ParamErr("文件ID不可为空!", fmt.Errorf("文件ID不可为空")) } // 获取当前用户ID currentUserId := c.GetString("id") if currentUserId == "" { return serializer.ParamErr("用户信息获取失败!", nil) } // 获取文件信息 var deployFile model.SysDeployFile if err := s.Db.Where("file_id = ? AND del_flag = ? AND create_by = ?", fileId, "0", currentUserId).First(&deployFile).Error; err != nil { logger.Error(c, "文件不存在或无权限访问!") return serializer.ParamErr("文件不存在或无权限访问!", err) } // 开始事务 tx := s.Db.Begin() // 将同项目下的其他文件设为未使用状态 if err := tx.Model(&model.SysDeployFile{}). Where("parent_id = ? AND del_flag = ? AND create_by = ?", deployFile.ParentId, "0", currentUserId). Updates(map[string]any{ "status": model.FileStatusNotUsed, "update_time": time.Now(), "update_by": currentUserId, }).Error; err != nil { tx.Rollback() logger.Error(c, "更新文件状态失败!") return serializer.DBErr("更新文件状态失败!", err) } // 将当前文件设为使用中状态 if err := tx.Model(&model.SysDeployFile{}). Where("file_id = ?", fileId). Updates(map[string]any{ "status": model.FileStatusInUse, "update_time": time.Now(), "update_by": currentUserId, }).Error; err != nil { tx.Rollback() logger.Error(c, "设置活跃文件失败!") return serializer.DBErr("设置活跃文件失败!", err) } tx.Commit() return serializer.Succ("设置活跃文件成功!", nil) } // GetActiveFileByParentID 根据项目ID获取当前使用中的文件 func (s *SysDeployFileService) GetActiveFileByParentID(parentId string) (*model.SysDeployFile, error) { var deployFile model.SysDeployFile err := s.Db.Where("parent_id = ? AND status = ? AND del_flag = ?", parentId, model.FileStatusInUse, "0").First(&deployFile).Error if err != nil { return nil, err } return &deployFile, nil } // CalculateFileHash 计算文件哈希值 func (s *SysDeployFileService) CalculateFileHash(reader io.Reader) (string, error) { hash := md5.New() if _, err := io.Copy(hash, reader); err != nil { return "", err } return fmt.Sprintf("%x", hash.Sum(nil)), nil }