// Package admin // @Description // @Author Ms <133814250@qq.com> package admin import ( "context" "encoding/json" "fmt" "github.com/bndr/gojenkins" "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/glog" "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/text/gstr" "github.com/gogf/gf/v2/util/gconv" "github.com/gogf/gf/v2/util/grand" "github.com/gogf/gf/v2/util/guid" "hotgo/internal/dao" "hotgo/internal/library/contexts" "hotgo/internal/model" "hotgo/internal/model/entity" "hotgo/internal/model/input/adminin" "hotgo/internal/service" "hotgo/internal/utils" "io" "net/http" "os" "path/filepath" "strings" ) type sAdminProject struct{} func NewAdminProject() *sAdminProject { return &sAdminProject{} } func init() { service.RegisterAdminProject(NewAdminProject()) } const ( DEPLOY_BUILD_KEY = "deploy.build" DEPLOY_BUILD_URL_KEY = DEPLOY_BUILD_KEY + "." + "url" DEPLOY_BUILD_JOBNAME_KEY = DEPLOY_BUILD_KEY + "." + "jobName" DEPLOY_BUILD_JOBDOWN_KEY = DEPLOY_BUILD_KEY + "." + "jobdown" DEPLOY_BUILD_CALLBACK_KEY = DEPLOY_BUILD_KEY + "." + "callback" DEPLOY_BUILD_MINIOPATH_KEY = DEPLOY_BUILD_KEY + "." + "ossPathPre" DEPLOY_BUILD_UPLOAD_KEY = DEPLOY_BUILD_KEY + "." + "upload" DEPLOY_BUILD_CONFIG_PATH = DEPLOY_BUILD_KEY + "." + "confPath" DEPLOY_BUILD_FRONT_PATH = DEPLOY_BUILD_KEY + "." + "frontPath" DEPLOY_BUILD_JOBTOKEN_KEY = DEPLOY_BUILD_KEY + "." + "jobToken" DEPLOY_BUILD_JOBUSER_KEY = DEPLOY_BUILD_KEY + "." + "jobUser" DEPLOY_BUILD_JOBPASSWORD_KEY = DEPLOY_BUILD_KEY + "." + "jobPassword" ) // Model ORM模型 func (s *sAdminProject) Model(ctx context.Context) *gdb.Model { return dao.AdminProject.Ctx(ctx).Handler(func(m *gdb.Model) *gdb.Model { return m.Where(dao.AdminProject.Columns().CreatedBy, contexts.GetUserId(ctx)) }) } // List 查询列表 func (s *sAdminProject) List(ctx context.Context, in *adminin.ProjectListInp) (list []*adminin.ProjectListModel, totalCount int, err error) { mod := s.Model(ctx) totalCount, err = mod.Count() if err != nil { return } if totalCount == 0 { return } if err = mod.Page(in.Page, in.PerPage).OrderDesc(dao.AdminProject.Columns().Id).Scan(&list); err != nil { return } for _, v := range list { v.State = v.Status v.CreateUserId = v.CreatedBy v.CreateTime = v.CreatedAt } return } // Delete 删除 func (s *sAdminProject) Delete(ctx context.Context, in *adminin.ProjectDeleteInp) (err error) { if exists, err := s.ExistById(ctx, in.Id); err != nil || !exists { err = gerror.New("没有找到项目!") return err } var project *entity.AdminProject err = s.Model(ctx).WherePri(in.Id).Scan(&project) if project.Status == 1 { err = gerror.New("项目已发布,无法删除!") return err } _, err = s.Model(ctx).WherePri(in.Id).Delete() return } // Create 新增 func (s *sAdminProject) Create(ctx context.Context, in *adminin.ProjectCreateInp) (res *adminin.ProjectCreateModel, err error) { data := &entity.AdminProject{ Id: gconv.Int64(fmt.Sprintf("%v%v", gtime.Now().Format("YmdH"), grand.N(10000, 99999))), ProjectName: in.ProjectName, IndexImage: in.IndexImage, Status: -1, Remarks: in.Remarks, CreatedBy: contexts.GetUserId(ctx), } if _, err = dao.AdminProject.Ctx(ctx).Data(data).Insert(); err != nil { return nil, err } res = &adminin.ProjectCreateModel{ Id: data.Id, ProjectName: data.ProjectName, State: data.Status, CreateTime: gtime.Now(), CreateUserId: contexts.GetUserId(ctx), IndexImage: data.IndexImage, Remarks: data.Remarks, } return } // Edit 修改 func (s *sAdminProject) Edit(ctx context.Context, in *adminin.ProjectEditInp) (err error) { if exists, err := s.ExistById(ctx, in.Id); err != nil || !exists { err = gerror.New("没有找到项目!") return err } data := make(g.Map) if len(in.IndexImage) > 0 { data["index_image"] = in.IndexImage } if len(in.ProjectName) > 0 { data["project_name"] = in.ProjectName } if len(in.Remarks) > 0 { data["remarks"] = in.Remarks } if len(data) > 0 { _, err = s.Model(ctx).WherePri(in.Id).Data(data).Update() } return } // SaveData 保存数据 func (s *sAdminProject) SaveData(ctx context.Context, in *adminin.ProjectSaveDataInp) (err error) { if exists, err := s.ExistById(ctx, in.Id); err != nil || !exists { err = gerror.New("没有找到项目!") return err } _, err = s.Model(ctx).WherePri(in.Id).Data("content", in.Content).Update() return } // Publish 修改发布状态 func (s *sAdminProject) Publish(ctx context.Context, in *adminin.ProjectPublishInp) (err error) { if exists, err := s.ExistById(ctx, in.Id); err != nil || !exists { err = gerror.New("没有找到项目!") return err } _, err = s.Model(ctx).WherePri(in.Id).Data("status", in.State).Update() return } // GetData 获取指定信息 func (s *sAdminProject) GetData(ctx context.Context, in *adminin.ProjectGetDataInp) (res *adminin.ProjectGetDataModel, err error) { if err = dao.AdminProject.Ctx(ctx).WherePri(in.Id).Scan(&res); err != nil { return nil, err } if res == nil { err = gerror.New("项目不存在") return } res.State = res.Status return } // ExistById 判断指定项目是否存在 func (s *sAdminProject) ExistById(ctx context.Context, id int64) (exists bool, err error) { count, err := s.Model(ctx).WherePri(id).Count() if err != nil { return } exists = count > 0 return } func (s *sAdminProject) Deploy(ctx context.Context, in *adminin.ProjectDeployInp) (err error) { var tempName = guid.S() var basePath = "resource/tmp/" + tempName var project *entity.AdminProject var jsonObj *model.JsonObj err = s.Model(ctx).WherePri(in.Id).Scan(&project) if project == nil { err = gerror.New("项目不存在") return } gconv.Struct(project.Content, &jsonObj) fmt.Println(jsonObj.EditCanvasConfig.BackgroundImage) if jsonObj.EditCanvasConfig.BackgroundImage != "" { backimageUrl := gconv.String(jsonObj.EditCanvasConfig.BackgroundImage) urlarr := strings.Split(backimageUrl, "?") filename := filepath.Base(urlarr[0]) DownloadImage(ctx, urlarr[0], basePath+"/data/image/") jsonObj.EditCanvasConfig.BackgroundImage = "/data/image/" + filename + "?" + urlarr[1] } componentList := jsonObj.ComponentList for _, component := range componentList { if component.Key == "Image" && component.Option.Dataset != nil { fmt.Println("Image") fmt.Println(component.Option.Dataset) fileUrl := gconv.String(component.Option.Dataset) filename := filepath.Base(fileUrl) DownloadImage(ctx, fileUrl, basePath+"/data/image/") component.Option.Dataset = "/data/image/" + filename } if component.Key == "Video" && component.Option.Dataset != nil { fmt.Println("Video") fmt.Println(component.Option.Dataset) fileUrl := gconv.String(component.Option.Dataset) filename := filepath.Base(fileUrl) DownloadImage(ctx, fileUrl, basePath+"/data/video/") component.Option.Dataset = "/data/video/" + filename } if component.Key == "ImageCarousel" && component.Option.Dataset != nil { fmt.Println("ImageCarousel") fmt.Println(component.Option.Dataset) var datalist = gconv.SliceStr(component.Option.Dataset) for i, fileUrl := range datalist { filename := filepath.Base(fileUrl) DownloadImage(ctx, fileUrl, basePath+"/data/image/") datalist[i] = "/data/image/" + filename } component.Option.Dataset = datalist } } jsonstr, err := json.Marshal(jsonObj) os.WriteFile(basePath+"/project.json", jsonstr, 0666) err = utils.ZipCompress(basePath, basePath+".zip") if err != nil { return err } g.Log().Debug(ctx, "压缩文件完成") s.BuildTask(ctx, in.Url, basePath+".zip", in.Id) //serverFile := basePath + tempName + ".zip" return nil } func DownloadImage(ctx context.Context, url string, path string) error { // 发送 GET 请求获取图片 resp, err := http.Get(url) if err != nil { g.Log().Error(ctx, err) return fmt.Errorf("failed to fetch the image: %w", err) } defer resp.Body.Close() // 检查响应状态码 if resp.StatusCode != http.StatusOK { g.Log().Error(ctx, err) return fmt.Errorf("request returned status code %d", resp.StatusCode) } // 获取图片扩展名 filename := filepath.Base(url) // 确保目录存在 err = os.MkdirAll(path, os.ModePerm) if err != nil { g.Log().Error(ctx, err) return fmt.Errorf("failed to create directory: %w", err) } // 打开或创建文件 file, err := os.Create(path + filename) if err != nil { g.Log().Error(ctx, err) return fmt.Errorf("failed to create file: %w", err) } defer file.Close() // 将图片数据写入文件 _, err = io.Copy(file, resp.Body) if err != nil { g.Log().Error(ctx, err) return fmt.Errorf("failed to write data to file: %w", err) } return nil } func (s *sAdminProject) BuildTask(ctx context.Context, options string, resource string, projectId int64) (err error) { serviceurl := s.GetCfgEnv(ctx, DEPLOY_BUILD_URL_KEY) jobName := s.GetCfgEnv(ctx, DEPLOY_BUILD_JOBNAME_KEY) callback := s.GetCfgEnv(ctx, DEPLOY_BUILD_CALLBACK_KEY) uploadPath := s.GetCfgEnv(ctx, DEPLOY_BUILD_UPLOAD_KEY) last := strings.LastIndex(resource, "/") if last > 0 { filename := resource var uploadFile = "" if gstr.ToLower("windows") == "linux" { uploadFile = "bin/linux_amd64/hc-base-gf" } else { uploadFile = "bin/windows_amd64/hc-base-gf.exe" } paras := map[string]string{ "jobName": jobName, "remote_path": "obj.ResUrl", "project_id": gconv.String(projectId), // "target_path": "target/project", "target_path": "/projects/1panel/1panel/apps/jenkins/jenkins/data/_data/jenkins_output", "file_name": filename, "callback_url": callback, //"http://192.168.10.241:30621/port/getJobBuildBack", "upload_path": uploadPath, "deploy_env": "windows", "auto_open_enable": "true", "upload_file": uploadFile, "front_path": "", } num, err, errMsg := s.buildJenkinsJob(ctx, serviceurl, paras, jobName) g.Log().Info(ctx, fmt.Sprintf("构建任务:%s, num:%d, err:%s, errMsg:%s", jobName, num, err, errMsg)) } return err } func (s *sAdminProject) GetCfgEnv(ctx context.Context, key string) string { v, _ := g.Cfg().GetWithEnv(ctx, key) return v.String() } func (s *sAdminProject) buildJenkinsJob(ctx context.Context, baseUrl string, parameters map[string]string, pipelineJobName string) (num int64, err error, errMsg string) { glog.Info(ctx, fmt.Sprintf("开始构建部署任务:baseUrl:%s, parameters:%s,pipelineJobName:%s", baseUrl, parameters, pipelineJobName)) // 替换为有效的用户名和 API Token jobuser := s.GetCfgEnv(ctx, DEPLOY_BUILD_JOBUSER_KEY) jobpassword := s.GetCfgEnv(ctx, DEPLOY_BUILD_JOBPASSWORD_KEY) // 创建 HTTP 客户端 client := &http.Client{} glog.Info(ctx, fmt.Sprintf("初始化Jenkins客户端:baseUrl:%s,用户:%s", baseUrl, jobuser)) jenkins, err := gojenkins.CreateJenkins(client, baseUrl, jobuser, jobpassword).Init(ctx) if err != nil { errMsg = fmt.Sprintf("Jenkins 客户端初始化失败:baseUrl:%s,用户:%s", baseUrl, jobuser) glog.Errorf(ctx, errMsg, err) return 0, err, errMsg } jobarr := strings.Split(pipelineJobName, "/") newjobname := strings.Join(jobarr, "/job/") glog.Info(ctx, fmt.Sprintf("触发流水线执行,任务名:%s", newjobname)) queId, err := jenkins.BuildJob(ctx, newjobname, parameters) glog.Debug(ctx, queId) if err != nil { errMsg = fmt.Sprintf("触发流水线执行失败:流水线名:%s, 错误信息:%s", newjobname, err.Error()) glog.Error(ctx, errMsg, err) return 0, err, errMsg } //通过队列号获取不到构建,只用来刷新构建号获取值 build, err := jenkins.GetBuildFromQueueID(ctx, queId) if build != nil { glog.Debug(ctx, build.GetBuildNumber()) } else { glog.Debug(ctx, queId) } job, err := jenkins.GetJob(ctx, newjobname) lastbuild, err := job.GetLastBuild(ctx) glog.Debug(ctx, "流水线执行已触发,构建号:", lastbuild.GetBuildNumber()) glog.Info(ctx, "部署任务执行成功,构建号:", lastbuild.GetBuildNumber()) return lastbuild.GetBuildNumber(), err, "" }